API security scanning,
from the command line.
Sentinel runs passive and controlled active checks against your HTTP APIs and produces structured JSON and Markdown reports. Built for local dev and CI.
Built around the OWASP API Top 10
Passive checks run on every scan. Active checks are rate-limited and capped per suite. Injection is opt-in and requires an OpenAPI spec.
HTTP Security Headers
Checks for HSTS, X-Content-Type-Options, and Referrer-Policy. Flags missing or misconfigured headers that leave responses open to MIME-sniffing, clickjacking, and downgrade attacks.
CORS Misconfiguration
Detects wildcard origins, origin-reflection vulnerabilities, and the dangerous wildcard-plus-credentials combination that browsers permit but which exposes authenticated resources cross-origin.
Auth Behavior
Probes 401 semantics, auth bypass heuristics, and cross-origin redirect risk. Inspects JWTs for alg:none, missing exp, expired issuance, and excessive TTL — without modifying any state.
Rate Limiting Detection
Inspects standard and vendor rate-limit headers. Runs a controlled sequential burst probe and checks for Retry-After coverage. Warns when no throttling signals are present.
API Inventory
Probes for sensitive endpoint exposure — /debug, /actuator, /swagger, /admin, and similar. Cross-references discovered paths against your OpenAPI spec to surface stale or untracked versions.
Injection Probes
Disabled by default and requires an OpenAPI spec. Probes SQL, NoSQL, template, and (opt-in) command injection via output-based detection only — no time-based payloads, no side effects.
Designed for CI
Exit code 2 when high or critical findings are present. JSON and Markdown reports written to ./sentinel-out/. OpenAPI-driven endpoint selection keeps scope tight.
A deliberately broken API to practice on
We host a small intentionally-misconfigured API at target.barbel.dev. Point Sentinel at it to see real findings — every misconfiguration is genuine.
Exposed endpoints
target.barbel.dev/api/v2/usersAuth probe target. No auth enforcement by default.
target.barbel.dev/api/v2/authIssues JWTs — alg:none and long TTL by default.
target.barbel.dev/debugLive config dump. Always exposed.
target.barbel.dev/api/v1/usersStale version endpoint. Still responding.
Scan it
sentinel scan \ -u https://target.barbel.dev
A security gate on every PR
Weir is a GitHub Actions reusable workflow that runs Sentinel as a Fargate sidecar alongside your target API: one workflow call, no infrastructure to manage per repo.
PR opened
Weir's reusable workflow is invoked. OIDC federates to AWS — no stored credentials in the repo.
Fargate task launches
Target and Sentinel containers share loopback. Sentinel scans http://localhost:3000 inside a private subnet with no internet route.
Report gates the PR
Sentinel uploads its JSON report to S3. Weir reads it and exits nonzero on high/critical findings, failing the PR check.
Fargate task · private subnet · no internet route
# .github/workflows/your-ci.ymljobs: security: uses: uncommon-carp/weir/.github/workflows/scan.yml@main permissions: id-token: write # OIDC — no stored AWS keys contents: read
No stored credentials
OIDC web identity token — GitHub Actions federate to AWS at runtime.
No internet egress
Private subnet with VPC endpoints only (ECR, S3, CloudWatch Logs).
Loopback-only network
The two containers share a network namespace — no service discovery needed.
Automatic teardown
EventBridge backstop stops the task even if the workflow is cancelled mid-run.
Up and scanning in under a minute
Install
npm install -g @uncommon-carp/sentinel
Scan
sentinel scan -u https://api.example.com
Collect reports
./sentinel-out/ sentinel-report.json sentinel-report.md
Optional config file
Drop a sentinel.config.json at the root of your project. All fields are optional except target.baseUrl. CLI flags always take precedence. Secrets can be interpolated with "${VAR}".
--url / -uBase URL of the target (required)--openapiOpenAPI spec for endpoint enumeration + injection--config / -cPath to config file--out / -oOutput directory (default: ./sentinel-out)--verbose / -vVerbose logging{ "target": { "baseUrl": "https://api.example.com", "openapi": "./openapi.json" }, "auth": { "type": "bearer", "bearerToken": "${API_TOKEN}" }, "suites": { "injection": true }, "scope": { "enabled": true, "methods": ["get", "head"], "maxEndpoints": 20, "excludePaths": ["^/admin"] }}