npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@houysengleang/apicheck

v0.1.0

Published

Non-destructive CLI that scans your own HTTP APIs for common security and hygiene problems, mapped to the OWASP API Security Top 10.

Readme

apicheck

Catch the boring-but-critical security holes in your own APIs before you ship — mapped to the OWASP API Security Top 10, non-destructive by design.

License: MIT Node TypeScript PRs welcome

A small, non-destructive command-line tool that scans your own HTTP APIs for common security and hygiene problems — mapped to the OWASP API Security Top 10. Point it at a base URL plus an OpenAPI spec; it auto-discovers every endpoint and runs 11 checks across 6 OWASP categories, reporting to your terminal, as JSON, or as SARIF for CI dashboards.

It fills the gap between "run curl by hand" and heavyweight scanners like OWASP ZAP.

⚠️ Only scan systems you own or are explicitly authorized to test. apicheck observes and reports; it never exploits, modifies, or exfiltrates data. You must pass --i-am-authorized to run a scan.

Install

npm install -g @hoysengleang/apicheck   # installs the `apicheck` command
# or run without installing:
npx @hoysengleang/apicheck scan …

From source (for development):

npm install        # dev deps
npm run build      # produces dist/
npm link           # optional: makes `apicheck` available globally

Requires Node.js 20+.

Usage

apicheck scan <baseUrl> --spec <openapi.json|url> --i-am-authorized [options]

Example:

apicheck scan https://staging.example.com \
  --spec ./openapi.json \
  --token-a "$TOKEN_A" \
  --token-b "$TOKEN_B" \
  --i-am-authorized

baseUrl and --spec may instead come from a config file.

Options:

| Flag | Meaning | |------|---------| | --spec <path> | OpenAPI/Swagger spec (file path or URL) — required | | --i-am-authorized | Affirm you may scan the target — required | | --token-a, --token-b | Bearer tokens for two identities (auth / IDOR checks) | | --auth-header-a, --auth-header-b | Custom auth header per identity, "Name: Value" (repeatable) — for API-key/cookie/custom schemes | | --include <glob> | Only scan paths matching this glob (repeatable) | | --exclude <glob> | Skip paths matching this glob (repeatable) | | --concurrency <n> | Max concurrent requests (default 8, max 20) | | --rate-limit-burst <n> | Burst size for the rate-limit check (default 20, max 100) | | --timeout <ms> | Per-request timeout (default 10000) | | --fail-on <fail\|warn> | Exit non-zero on this severity or higher (default fail) | | --json | Emit machine-readable JSON to stdout (for CI) | | --sarif | Emit SARIF 2.1.0 to stdout (for code-scanning dashboards) | | --show-passes | Also print passing findings | | --config <path> | Config file (default apicheck.config.json) | | --ignore-file <path> | Suppression file (default .apicheckignore) |

Globs support * (within a path segment), ** (across segments), and ?. --exclude wins over --include, and both also scope the shadow-endpoint probe. --sarif takes precedence over --json; in both machine-output modes stdout is a single valid document (spinner/logs go to stderr).

Exit code is 1 when any finding meets --fail-on, so it works as a CI gate. Configuration or spec errors exit 2.

Config file

Commit shared settings instead of passing long flag lines. apicheck reads apicheck.config.json from the working directory (or --config <path>); CLI flags override the file.

{
  "baseUrl": "https://staging.example.com",
  "specPath": "./openapi.json",
  "tokens": { "a": "…", "b": "…" },
  "authHeaders": { "a": { "X-API-Key": "…" } },
  "include": ["/v1/**"],
  "exclude": ["/admin/**"],
  "ignore": ["rate-limit:GET /health"],
  "rateLimitBurst": 20,
  "failOn": "warn",
  "authorized": true
}

Suppressing findings

Keep a gate green against known/accepted issues with .apicheckignore (or --ignore-file, or an ignore array in the config). Each rule is <check>:<endpointGlob>; a bare <check> suppresses it everywhere. Suppressed findings are dropped before the summary and the exit code.

# .apicheckignore
rate-limit:GET /health        # health is intentionally unlimited
cors:*                        # CORS handled at the gateway
*:GET /legacy                 # legacy route, scheduled for removal

Checks

Per-endpoint checks:

| Check | OWASP | What it finds | |-------|-------|---------------| | security-headers | API8:2023 | Missing HSTS / nosniff / frame protection / cache-control | | missing-auth | API5:2023 / API2:2023 | Auth-required endpoints returning data unauthenticated | | rate-limit | API4:2023 | No throttling under a bounded (unpaced, hard-capped) burst | | idor | API1:2023 | Token B reading token A's object (broken object-level auth) | | excessive-data | API3:2023 | Sensitive fields (password, secret, token, …) in responses | | cors | API8:2023 | Reflective / wildcard CORS (worst with credentials) | | cookie-flags | API8:2023 | Set-Cookie missing Secure / HttpOnly / SameSite | | info-disclosure | API8:2023 | Server/version-leaking headers and leaked stack traces | | bfla | API5:2023 | Lower-privilege identity reaching privileged functions |

Scan-level checks (run once over the whole API):

| Check | OWASP | What it finds | |-------|-------|---------------| | shadow-endpoints | API9:2023 | Undocumented paths that respond (curated, bounded probe) | | inventory-drift | API9:2023 | Deprecated-but-published operations and API-version sprawl |

That's 11 checks across 6 of the OWASP API Top 10 (API1, API3, API4, API5, API8, API9).

Injection, SSRF, brute-force, and business-logic abuse are intentionally out of scope — they require sending attack payloads, which conflicts with the non-destructive model. See docs/safety.md.

Example output

⚠ GET /users        missing security headers: X-Content-Type-Options: nosniff (API8:2023) [security-headers]
✖ GET /users        returned 200 with no Authorization header — endpoint should require auth (API5:2023) [missing-auth]
✔ GET /health       rate limiting observed within 5 requests (API4:2023) [rate-limit]
✖ GET /users/{id}   token B read token A's resource at /users/alice (identical response body) — broken object level authorization (API1:2023) [idor]

Scanned 5 endpoints · 5 fail · 5 warn · 3 pass

CI integration

Exit code 1 on findings makes apicheck a gate in any pipeline. With --sarif you can also feed results into GitHub code scanning so they show up in the Security tab and as PR annotations:

# .github/workflows/api-scan.yml
- run: npx @hoysengleang/apicheck scan "$STAGING_URL" --spec openapi.json
       --token-a "$TOKEN_A" --token-b "$TOKEN_B"
       --i-am-authorized --sarif > apicheck.sarif
  continue-on-error: true          # let the upload run even when findings fail the step
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: apicheck.sarif

Development

npm run dev -- scan http://127.0.0.1:4010 --spec ./test/fixtures/openapi.json --i-am-authorized
npm test          # vitest (unit + end-to-end against a local mock API)
npm run typecheck # tsc --noEmit (strict)
npm run lint      # eslint
npm run build     # tsup -> dist/

The end-to-end test spins up a deliberately-flawed mock API (test/e2e/mock-api.ts) and asserts that each check produces the expected finding. To poke at it by hand:

npx tsx test/e2e/serve.ts 4010     # start the mock API
node dist/index.js scan http://127.0.0.1:4010 \
  --spec test/fixtures/openapi.json \
  --token-a TOKEN_A --token-b TOKEN_B --i-am-authorized --show-passes

Architecture

One data flow, three independent stages: discover → run checks → report.

OpenAPI spec ─▶ discovery ─▶ Endpoint[] ─▶ [ Check … ] ─▶ Finding[] ─▶ report (terminal + json)
                                              ▲
                                        HttpClient + ScanConfig

| Path | Responsibility | |------|----------------| | src/index.ts | CLI entry (commander) + scan orchestration | | src/runner.ts | Runs each check over each endpoint, collects findings | | src/config.ts | zod-validated ScanConfig + authorization gate | | src/discovery/openapi.ts | Spec → normalized Endpoint[] | | src/http/client.ts | undici wrapper: auth, timeout, retry, self-throttle, safe-method guard | | src/checks/* | One file per check, all implementing the Check contract | | src/report/* | Terminal (chalk) and JSON reporters |

The full design, conventions, and safety rationale live in docs/ (SKILL.md, architecture.md, checks.md, safety.md). Adding a check is one new file in src/checks/ plus one line in src/checks/index.ts.

Safety model

apicheck is observational and non-destructive by design:

  • Checks observe and report — never exploit, modify, or exfiltrate.
  • The HTTP client permits only GET / HEAD / OPTIONS by default; any other method requires the separate, greppable unsafeRequest() (which v1 checks never call).
  • Authorization is affirmed via --i-am-authorized and enforced structurally in config validation.
  • The scanner self-throttles; the rate-limit check is the one intentional, hard-capped (≤100) burst, and never targets a mutating endpoint.

See docs/safety.md for the full rationale.

Contributing

Contributions are welcome — especially new checks. A check is one file + one registry line + one test; scaffold it with:

npm run new-check -- <kebab-name> <OWASP-ref>   # e.g. verbose-errors API8:2023

See CONTRIBUTING.md for the full recipe, the safety rules every check must honor, and the dev workflow. Please also read our Code of Conduct. To report a security issue in apicheck itself, see SECURITY.md.

License

MIT