{"_id":"@api-common/spectral-owasp-ruleset","_rev":"2-a3b4b12722da2e7f9230d82513ff287f","name":"@api-common/spectral-owasp-ruleset","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@api-common/spectral-owasp-ruleset","version":"0.1.0","keywords":["spectral","owasp","api-security","owasp-api-top10","ruleset","governance","openapi","linting","api-commons"],"author":{"url":"https://apievangelist.com","name":"API Evangelist"},"license":"Apache-2.0","_id":"@api-common/spectral-owasp-ruleset@0.1.0","maintainers":[{"name":"api-commons","email":"info@apicommons.org"}],"homepage":"https://apicommons.org/tools/","bugs":{"url":"https://github.com/api-commons/spectral-owasp-ruleset/issues"},"dist":{"shasum":"c4336225f3ed19c60f86bc478bf1fceb930c4490","tarball":"https://registry.npmjs.org/@api-common/spectral-owasp-ruleset/-/spectral-owasp-ruleset-0.1.0.tgz","fileCount":6,"integrity":"sha512-6U4WCOqxijsm909b7/UB5exz/neoGTHSmhL0BYq7jma4VpcPHZE2cdkZfqZeWlEGZN1IuBIlErQxtrdEcPLCeg==","signatures":[{"sig":"MEUCIHoywfwDQYO9H5EDd93KNrR3HazbKoPoPYpfZ30ao7DzAiEAsgSnotnL4vk+qcYq/6c3hKiNPP9PrhZxki0QShOueSQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50175},"main":"owasp-api-top10.yaml","type":"module","engines":{"node":">=18"},"exports":{".":"./owasp-api-top10.yaml"},"gitHead":"82e80d4934c77cb5c6a8c3276af137df473947bf","scripts":{"test":"node scripts/test-ruleset.mjs","lint:clean":"spectral lint fixtures/clean.yaml -r owasp-api-top10.yaml -f json","lint:insecure":"spectral lint fixtures/insecure.yaml -r owasp-api-top10.yaml -f json"},"_npmUser":{"name":"api-commons","email":"info@apicommons.org"},"repository":{"url":"git+https://github.com/api-commons/spectral-owasp-ruleset.git","type":"git"},"_npmVersion":"11.6.2","description":"A curated, owned, grounded Stoplight Spectral ruleset for the OWASP API Security Top 10 (2023) — add a real security governance layer to your OpenAPI linting in one line. An API Commons tool.","directories":{},"_nodeVersion":"25.2.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@stoplight/spectral-cli":"^6.11.1"},"_npmOperationalInternal":{"tmp":"tmp/spectral-owasp-ruleset_0.1.0_1783113461944_0.15834782333446085","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@api-common/spectral-owasp-ruleset","version":"0.2.0","description":"A curated, owned, grounded Stoplight Spectral ruleset for the OWASP API Security Top 10 (2023) — add a real security governance layer to your OpenAPI linting in one line. An API Commons tool.","type":"module","license":"Apache-2.0","author":{"name":"API Evangelist","url":"https://apievangelist.com"},"homepage":"https://apicommons.org/tools/","repository":{"type":"git","url":"git+https://github.com/api-commons/spectral-owasp-ruleset.git"},"bugs":{"url":"https://github.com/api-commons/spectral-owasp-ruleset/issues"},"keywords":["spectral","owasp","api-security","owasp-api-top10","ruleset","governance","openapi","linting","api-commons"],"main":"owasp-api-top10.yaml","exports":{".":"./owasp-api-top10.yaml"},"engines":{"node":">=18"},"scripts":{"lint:insecure":"spectral lint fixtures/insecure.yaml -r owasp-api-top10.yaml -f json","lint:clean":"spectral lint fixtures/clean.yaml -r owasp-api-top10.yaml -f json","lint:insecure:oas2":"spectral lint fixtures/insecure-oas2.yaml -r owasp-api-top10.yaml -f json","lint:clean:oas2":"spectral lint fixtures/clean-oas2.yaml -r owasp-api-top10.yaml -f json","test":"node scripts/test-ruleset.mjs"},"devDependencies":{"@stoplight/spectral-cli":"^6.11.1"},"publishConfig":{"access":"public"},"gitHead":"7eb64eebafadae7e21cc05dbab97e58ac898bae5","_id":"@api-common/spectral-owasp-ruleset@0.2.0","_nodeVersion":"25.2.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-pQjBzTJeRq6ve8FH7f+upLdrOKdD1XvANe70nF2pj/weIc2Zq6SQG7oYoEnBiaI521Z3urtQuWess+9WHlHgRg==","shasum":"ae76072e252df4cf6257ff760b3403477be96103","tarball":"https://registry.npmjs.org/@api-common/spectral-owasp-ruleset/-/spectral-owasp-ruleset-0.2.0.tgz","fileCount":8,"unpackedSize":65700,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD1Vx75mJ1OUbcKdN4nWfzh1HI/Krmmn2ek8Mv4CkD03QIhAIgJIFarYPNuxEpRoYgSmPWahJos2odu9vqFqKbv6e9o"}]},"_npmUser":{"name":"api-commons","email":"info@apicommons.org"},"directories":{},"maintainers":[{"name":"api-commons","email":"info@apicommons.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/spectral-owasp-ruleset_0.2.0_1783117819757_0.006460736542990286"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-03T21:17:41.811Z","modified":"2026-07-03T22:30:20.014Z","0.1.0":"2026-07-03T21:17:42.085Z","0.2.0":"2026-07-03T22:30:19.895Z"},"bugs":{"url":"https://github.com/api-commons/spectral-owasp-ruleset/issues"},"author":{"name":"API Evangelist","url":"https://apievangelist.com"},"license":"Apache-2.0","homepage":"https://apicommons.org/tools/","keywords":["spectral","owasp","api-security","owasp-api-top10","ruleset","governance","openapi","linting","api-commons"],"repository":{"type":"git","url":"git+https://github.com/api-commons/spectral-owasp-ruleset.git"},"description":"A curated, owned, grounded Stoplight Spectral ruleset for the OWASP API Security Top 10 (2023) — add a real security governance layer to your OpenAPI linting in one line. An API Commons tool.","maintainers":[{"name":"api-commons","email":"info@apicommons.org"}],"readme":"# Spectral OWASP API Security Ruleset\n\n**A curated, owned, grounded [Stoplight Spectral](https://github.com/stoplightio/spectral) ruleset for the [OWASP API Security Top 10 (2023)](https://owasp.org/API-Security/editions/2023/en/0x11-t10/).**\n\n`@api-common/spectral-owasp-ruleset` lets any team add a real **security\ngovernance layer** to their OpenAPI linting **in one line**. It maps 22 OWASP\nchecks to all ten OWASP API Security categories, using only Spectral's\n**built-in functions** — no custom JavaScript — so it runs anywhere Spectral\nruns.\n\n**Swagger 2.0 and OpenAPI 3.x, at parity.** Spectral auto-detects a document's\nformat and each check runs on the shape it applies to. Format-agnostic checks\n(schema bounds, inventory metadata, global security, TRACE, external docs) carry\nno `formats` tag and fire on both. Where the two specs differ structurally —\nsecurity schemes (`components.securitySchemes` vs `securityDefinitions`),\ntransport (`servers` vs `host`/`schemes`), and request/response bodies\n(`content` media-type maps vs body parameters + response `schema`) — the 3.x\nrule is tagged `formats: [oas3]` and a `-oas2` twin carries the same OWASP\ngrounding for Swagger 2.0. Nothing false-positives or no-ops across formats.\n\nWhy this exists: a study of 1,005 public API pipelines found only **14%** run any\nsecurity rules at all, and just **3.4%** emit SARIF for their security tooling.\nThis ruleset closes that gap with an adoptable, **provenanced** set of rules —\nevery rule names the OWASP item it defends, explains the risk, and links to the\nsource.\n\nOne of the [API Commons tools](https://apicommons.org/tools/), alongside\n[Spectral Reporter](https://reporter.apicommons.org),\n[API Validator](https://validator.apicommons.org),\n[API Discovery](https://discover.apicommons.org),\n[API Documentation](https://documentation.apicommons.org),\n[API Reusability](https://reusability.apicommons.org), and\n[MCP Install](https://install.apicommons.org).\n\n## Grounded, owned rules\n\nEvery rule carries its provenance, modelling what a governance rule *should*\nlook like:\n\n- a stable, OWASP-mapped **id** (e.g. `owasp-api1-bola-operation-security-defined`)\n- a **description** of the risk it addresses\n- a **message** shown on each finding\n- a **severity** (`error` / `warn` / `info`)\n- a **documentationUrl** deep-linking the specific OWASP API Security Top 10 item\n\n## One-line adoption\n\n**Remote extends** — reference the ruleset by URL, no install (point at the\nraw file on your pinned tag/commit):\n\n```yaml\n# .spectral.yaml\nextends:\n  - \"https://raw.githubusercontent.com/api-commons/spectral-owasp-ruleset/main/owasp-api-top10.yaml\"\n```\n\n**Or install from npm** and extend by package name:\n\n```bash\nnpm i -D @api-common/spectral-owasp-ruleset\n```\n\n```yaml\n# .spectral.yaml\nextends:\n  - \"@api-common/spectral-owasp-ruleset\"\n```\n\nYou can layer it on top of the Spectral OpenAPI core rules:\n\n```yaml\nextends:\n  - \"spectral:oas\"\n  - \"@api-common/spectral-owasp-ruleset\"\n```\n\nThen lint:\n\n```bash\nnpx @stoplight/spectral-cli lint openapi.yaml\n```\n\n## What it checks — rule → OWASP item → severity\n\nThe OWASP API Security Top 10 is partly about **runtime** authorization and\nabuse decisions that a static OpenAPI document cannot fully express. Where an\nitem is directly lintable we ship a real check; where it is not, we ship the\nstrongest **static proxy** we honestly can (e.g. \"is auth even *declared* on\nthis operation?\") and mark the residual **advisory**. We do not fake coverage.\n\nThe **Formats** column shows where each check applies: **both** = one\nformat-agnostic rule; **3.x + 2.0 twin** = a `formats: [oas3]` rule paired with a\n`-oas2` twin (named below) that checks the equivalent Swagger 2.0 structure.\n\n| Rule id | OWASP item | What it checks | Severity | Coverage | Formats |\n| --- | --- | --- | --- | --- | --- |\n| `owasp-api1-bola-operation-security-defined` | API1 BOLA | Every operation declares a `security` requirement (object authz needs an authenticated request) | warn | proxy | both |\n| `owasp-api2-auth-security-schemes-defined` | API2 Broken Auth | `components.securitySchemes` defines ≥1 scheme | error | lintable | 3.x + `…-oas2` twin (`securityDefinitions`) |\n| `owasp-api2-auth-apikey-not-in-url` | API2 Broken Auth | API-key schemes use `in: header`/`cookie`, never `query`/`path` | error | lintable | 3.x + `…-oas2` twin (2.0: `in: header` only) |\n| `owasp-api2-auth-no-http-basic` | API2 Broken Auth | No HTTP Basic auth scheme | warn | lintable | 3.x + `…-oas2` twin (2.0: `type: basic`) |\n| `owasp-api2-auth-oauth2-https-urls` | API2 Broken Auth | OAuth2 authorization/token/refresh URLs are `https://` | error | lintable | 3.x + `…-oas2` twin (2.0: url on the scheme, no `flows`) |\n| `owasp-api3-bopla-response-schema-defined` | API3 BOPLA | Every response `content` declares a `schema` (exposed properties reviewable) | warn | proxy | 3.x + `…-oas2` twin (2.0: `responses[2xx].schema`) |\n| `owasp-api3-bopla-request-schema-defined` | API3 BOPLA | Every request `content` declares a `schema` (bounds mass assignment) | warn | proxy | 3.x + `…-oas2` twin (2.0: `in: body` param `schema`) |\n| `owasp-api4-resource-array-maxitems` | API4 Resource Consumption | Array schemas declare `maxItems` | warn | lintable | both |\n| `owasp-api4-resource-string-maxlength` | API4 Resource Consumption | String schemas declare `maxLength` | info | lintable | both |\n| `owasp-api4-resource-integer-bounds` | API4 Resource Consumption | Integer/number schemas declare `maximum` | info | lintable | both |\n| `owasp-api5-bfla-global-security-defined` | API5 BFLA | A top-level `security` baseline is declared (default-deny) | warn | proxy | both |\n| `owasp-api6-sensitive-flows-rate-limit-response` | API6 Sensitive Business Flows | State-changing ops document a `429` response (throttling) | info | proxy | both |\n| `owasp-api7-ssrf-url-property-format` | API7 SSRF | URL-bearing properties declare `format: uri` for review | info | proxy | both |\n| `owasp-api8-misconfig-https-servers` | API8 Security Misconfiguration | All `servers` URLs are `https://` | error | lintable | 3.x + `…-oas2` twin (2.0: `schemes` https/not http) |\n| `owasp-api8-misconfig-no-trace-method` | API8 Security Misconfiguration | No `trace` HTTP method (blocks XST) | error | lintable | both (TRACE is 3.x-only, so vacuous on 2.0) |\n| `owasp-api8-misconfig-servers-defined` | API8 Security Misconfiguration | `servers` is declared | warn | lintable | 3.x + `owasp-api8-misconfig-host-defined-oas2` (2.0: `host`) |\n| `owasp-api9-inventory-info-version` | API9 Inventory | `info.version` is present | error | lintable | both |\n| `owasp-api9-inventory-contact-defined` | API9 Inventory | `info.contact` is present (known owner) | warn | lintable | both |\n| `owasp-api9-inventory-operation-description` | API9 Inventory | Every operation has a `description` (no shadow endpoints) | warn | lintable | both |\n| `owasp-api9-inventory-operationid-defined` | API9 Inventory | Every operation has an `operationId` | warn | lintable | both |\n| `owasp-api9-inventory-servers-not-example` | API9 Inventory | Server URLs are not placeholders (`example.com`/`localhost`) | warn | lintable | 3.x + `owasp-api9-inventory-host-not-example-oas2` (2.0: `host`) |\n| `owasp-api10-consumption-externaldocs-https` | API10 Unsafe Consumption | `externalDocs.url` is `https://` | info | proxy | both |\n\n**22 OWASP checks, all 10 OWASP API Security Top 10 (2023) categories covered —\non both Swagger 2.0 and OpenAPI 3.x.** 13 checks are single format-agnostic\nrules; 9 are a `formats: [oas3]` rule plus a `-oas2` twin (31 rule entries in\n`owasp-api-top10.yaml`).\n\n### Lintable vs advisory, by OWASP item\n\n| OWASP item | Static coverage |\n| --- | --- |\n| **API1 BOLA** | **Proxy** — object-level authz is runtime; we require every operation to *declare* auth. |\n| **API2 Broken Authentication** | **Lintable** — scheme presence, API-key location, no Basic, HTTPS OAuth URLs. |\n| **API3 BOPLA** | **Proxy** — property-level authz is runtime; we require request/response schemas so exposed/writable properties are reviewable. |\n| **API4 Unrestricted Resource Consumption** | **Lintable** — `maxItems`/`maxLength`/`maximum` bounds on payloads. |\n| **API5 BFLA** | **Proxy** — function-level authz is runtime; we require a default-deny `security` baseline. |\n| **API6 Sensitive Business Flows** | **Proxy / advisory** — \"sensitive\" is business context; we nudge a `429` on state-changing ops. Confirm real throttling at the gateway. |\n| **API7 SSRF** | **Proxy / advisory** — whether the server fetches user URLs is invisible in the spec; we flag URL-bearing inputs for `format` + allow-list review. Real defense is runtime host allow-listing. |\n| **API8 Security Misconfiguration** | **Lintable** — HTTPS servers, no TRACE, servers declared. |\n| **API9 Improper Inventory Management** | **Lintable** — version, contact, per-operation description/operationId, no placeholder hosts. |\n| **API10 Unsafe Consumption of APIs** | **Proxy / advisory** — upstream consumption is invisible in the spec; we require HTTPS on the URLs the doc points others to. Validate third-party responses at runtime. |\n\nThe **advisory** residuals — object/property/function-level authorization\ndecisions (API1/API3/API5), sensitive-flow abuse (API6), SSRF allow-listing\n(API7), and third-party response validation (API10) — are runtime concerns.\nEnforce them in code, tests, and your gateway; this ruleset makes sure the\ndocument at least *exposes* them for review.\n\n## In GitHub Actions (dedicated security job + optional SARIF)\n\nRun the OWASP ruleset as its own gate, separate from your general style lint, so\na security regression is unambiguous — and (optionally) upload SARIF so findings\nshow up in the repo's **Security → Code scanning** tab.\n\n```yaml\nname: API Security Governance\non: [push, pull_request]\n\njobs:\n  owasp-api-security:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      # Fail the build on any OWASP error-severity finding.\n      - name: OWASP API Security lint\n        run: |\n          npx @stoplight/spectral-cli lint openapi.yaml \\\n            -r https://raw.githubusercontent.com/api-commons/spectral-owasp-ruleset/main/owasp-api-top10.yaml\n\n      # Optional: emit SARIF for GitHub code scanning (does not fail the job).\n      - name: OWASP API Security lint (SARIF)\n        if: always()\n        run: |\n          npx @stoplight/spectral-cli lint openapi.yaml \\\n            -r https://raw.githubusercontent.com/api-commons/spectral-owasp-ruleset/main/owasp-api-top10.yaml \\\n            -f sarif -o results.sarif || true\n\n      - name: Upload SARIF\n        if: always()\n        uses: github/codeql-action/upload-sarif@v3\n        with:\n          sarif_file: results.sarif\n          category: owasp-api-security\n```\n\nPin the raw URL to a tag or commit SHA (not `main`) for reproducible CI, or\ninstall the npm package and `extends: [\"@api-common/spectral-owasp-ruleset\"]`\nfrom a committed `.spectral.yaml`.\n\n## Try it locally\n\nThe repo ships four fixtures (a 3.x pair and a Swagger 2.0 pair) and a test that\nproves the ruleset actually fires on both formats:\n\n```bash\nnpm install                 # installs the Spectral CLI (devDependency)\nnpm test                    # lints both format pairs: insecure fires all 10\n                            # families, clean is silent, and no rule throws\n\n# Or lint the fixtures by hand:\nnpm run lint:insecure       # 3.x: 30 findings across 20 rules, all 10 families\nnpm run lint:clean          # 3.x: 0 findings\nnpm run lint:insecure:oas2  # 2.0: 30 findings across 19 rules, all 10 families\nnpm run lint:clean:oas2     # 2.0: 0 findings\n```\n\n`fixtures/insecure.yaml` (OpenAPI 3.x) and `fixtures/insecure-oas2.yaml`\n(Swagger 2.0) are intentionally-broken specs (API key in the query string, no\noperation `security`, plaintext transport, HTTP Basic, missing body/response\nschemas, missing inventory metadata). `fixtures/clean.yaml` and\n`fixtures/clean-oas2.yaml` are well-governed specs that pass.\n\n## How the rules are written\n\nEvery rule uses only Spectral's default functions —\n`defined`, `truthy`, `falsy`, `pattern`, `schema`, `enumeration`, `casing`,\n`length`, `alphabetical` — so there is nothing to install, audit, or trust\nbeyond Spectral itself. Read `owasp-api-top10.yaml`; it is heavily commented,\ngrouped by OWASP item, with the reasoning for each check and each advisory gap.\n\n## TODOs (for the human picking this up)\n\n- [ ] **Create + push the GitHub repo** `api-commons/spectral-owasp-ruleset`\n      (this tree is committed locally but has **no remote** yet — no push has\n      happened). Once pushed, the raw `extends` URLs above go live.\n- [ ] **npm publish** `@api-common/spectral-owasp-ruleset` (scope `@api-common`\n      is singular). `publishConfig.access` is already `public`; run\n      `npm publish` once the repo is up.\n- [ ] Consider tagging a release so the CI `extends` URL can pin a SHA/tag\n      instead of `main`.\n- [ ] Optionally publish a landing page under `apicommons.org/tools/` and add it\n      to the tools index.\n\n## License\n\n[Apache-2.0](./LICENSE) — free and open. Copyright 2026 API Commons (Kin Lane).\nA project of [API Evangelist](https://apievangelist.com), maintained under\n[API Commons](https://apicommons.org). API Evangelist offers expert\n[governance services](https://apievangelist.com/services/) when you want help\nstanding up API security governance.\n","readmeFilename":"README.md"}