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.

zsh
Six test suites

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.

HeadersAPI8

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.

CORSAPI2, API8

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.

AuthAPI2, API5

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 LimitAPI4

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.

InventoryAPI9

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.

Injectionopt-inAPI8

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.

exit 0 · 1 · 2 · 3
Demo target

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/users

Auth probe target. No auth enforcement by default.

target.barbel.dev/api/v2/auth

Issues JWTs — alg:none and long TTL by default.

target.barbel.dev/debug

Live config dump. Always exposed.

target.barbel.dev/api/v1/users

Stale version endpoint. Still responding.

Scan it

sentinel scan \
  -u https://target.barbel.dev
Intentionally vulnerable for demonstration. No real user data.
CI Gate

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.

01

PR opened

Weir's reusable workflow is invoked. OIDC federates to AWS — no stored credentials in the repo.

02

Fargate task launches

Target and Sentinel containers share loopback. Sentinel scans http://localhost:3000 inside a private subnet with no internet route.

03

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

├── target :3000 ← your API image
└── sentinel ← scans localhost:3000
└── writes s3://bucket/results/$RUN_ID.json
your-ci.yml
# .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
View Weir on GitHub

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.

Quickstart

Up and scanning in under a minute

01

Install

npm install -g @uncommon-carp/sentinel
02

Scan

sentinel scan -u https://api.example.com
03

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
sentinel.config.json
{  "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"]  }}