vanara 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +14 -0
  2. package/bin/vanara.js +66 -2
  3. package/catalog/agents/api-designer/AGENT.md +156 -0
  4. package/catalog/agents/api-designer/examples/openapi-snippet.yaml +130 -0
  5. package/catalog/agents/api-designer/examples/review-notes.md +61 -0
  6. package/catalog/agents/api-designer/references/contract-and-openapi.md +82 -0
  7. package/catalog/agents/api-designer/references/design-checklist.md +68 -0
  8. package/catalog/agents/api-designer/references/versioning-and-evolution.md +63 -0
  9. package/catalog/agents/api-designer/scripts/lint-openapi.mjs +180 -0
  10. package/catalog/agents/code-reviewer/AGENT.md +184 -0
  11. package/catalog/agents/code-reviewer/examples/pr-comment-template.md +48 -0
  12. package/catalog/agents/code-reviewer/examples/sample-review-output.md +38 -0
  13. package/catalog/agents/code-reviewer/references/review-checklist.md +46 -0
  14. package/catalog/agents/code-reviewer/references/security-review.md +62 -0
  15. package/catalog/agents/code-reviewer/references/severity-rubric.md +63 -0
  16. package/catalog/agents/code-reviewer/scripts/review-guard.mjs +102 -0
  17. package/catalog/agents/debugger/AGENT.md +128 -0
  18. package/catalog/agents/debugger/examples/postmortem-template.md +59 -0
  19. package/catalog/agents/debugger/examples/repro-template.md +50 -0
  20. package/catalog/agents/debugger/references/bisection.md +63 -0
  21. package/catalog/agents/debugger/references/debugging-method.md +57 -0
  22. package/catalog/agents/debugger/references/observability.md +61 -0
  23. package/catalog/agents/debugger/scripts/parse-stacktrace.mjs +109 -0
  24. package/catalog/agents/pr-summarizer/AGENT.md +172 -0
  25. package/catalog/agents/pr-summarizer/examples/sample-pr-summary.md +46 -0
  26. package/catalog/agents/pr-summarizer/examples/weak-vs-strong-summary.md +68 -0
  27. package/catalog/agents/pr-summarizer/references/identifying-risk.md +68 -0
  28. package/catalog/agents/pr-summarizer/references/reading-the-diff.md +68 -0
  29. package/catalog/agents/pr-summarizer/references/test-plan-and-missing-tests.md +64 -0
  30. package/catalog/agents/pr-summarizer/references/writing-the-summary.md +63 -0
  31. package/catalog/agents/pr-summarizer/scripts/diff-risk.mjs +111 -0
  32. package/catalog/agents/refactoring-specialist/AGENT.md +201 -0
  33. package/catalog/agents/refactoring-specialist/examples/before-after.md +91 -0
  34. package/catalog/agents/refactoring-specialist/examples/refactor-plan.md +97 -0
  35. package/catalog/agents/refactoring-specialist/references/large-scale-refactor.md +50 -0
  36. package/catalog/agents/refactoring-specialist/references/safe-workflow.md +75 -0
  37. package/catalog/agents/refactoring-specialist/references/smell-catalog.md +76 -0
  38. package/catalog/agents/refactoring-specialist/scripts/detect-smells.mjs +150 -0
  39. package/catalog/agents/security-auditor/AGENT.md +184 -0
  40. package/catalog/agents/security-auditor/examples/audit-report.md +68 -0
  41. package/catalog/agents/security-auditor/examples/finding-template.md +31 -0
  42. package/catalog/agents/security-auditor/references/audit-workflow.md +116 -0
  43. package/catalog/agents/security-auditor/references/severity-and-reporting.md +56 -0
  44. package/catalog/agents/security-auditor/references/vuln-classes.md +129 -0
  45. package/catalog/agents/security-auditor/scripts/scan-secrets.mjs +86 -0
  46. package/catalog/agents/technical-writer/AGENT.md +179 -0
  47. package/catalog/agents/technical-writer/examples/how-to-example.md +76 -0
  48. package/catalog/agents/technical-writer/examples/reference-example.md +75 -0
  49. package/catalog/agents/technical-writer/references/clarity-and-style.md +52 -0
  50. package/catalog/agents/technical-writer/references/diataxis-and-doc-types.md +61 -0
  51. package/catalog/agents/technical-writer/references/structure-and-maintenance.md +59 -0
  52. package/catalog/agents/technical-writer/scripts/readability.mjs +103 -0
  53. package/catalog/agents/test-author/AGENT.md +193 -0
  54. package/catalog/agents/test-author/examples/test-plan.md +54 -0
  55. package/catalog/agents/test-author/examples/unit-test-example.ts +55 -0
  56. package/catalog/agents/test-author/references/aaa-and-naming.md +75 -0
  57. package/catalog/agents/test-author/references/mocking-and-fixtures.md +94 -0
  58. package/catalog/agents/test-author/references/test-types-and-pyramid.md +60 -0
  59. package/catalog/agents/test-author/scripts/check-coverage.mjs +145 -0
  60. package/catalog/agents/threat-modeler/AGENT.md +179 -0
  61. package/catalog/agents/threat-modeler/examples/dfd-example.md +55 -0
  62. package/catalog/agents/threat-modeler/examples/threat-model.md +81 -0
  63. package/catalog/agents/threat-modeler/references/dfd-and-trust-boundaries.md +76 -0
  64. package/catalog/agents/threat-modeler/references/mitigation-catalog.md +77 -0
  65. package/catalog/agents/threat-modeler/references/stride-method.md +68 -0
  66. package/catalog/agents/threat-modeler/scripts/stride-checklist.mjs +183 -0
  67. package/catalog/agents/vuln-scanner/AGENT.md +162 -0
  68. package/catalog/agents/vuln-scanner/examples/finding-template.md +57 -0
  69. package/catalog/agents/vuln-scanner/examples/scan-report.md +49 -0
  70. package/catalog/agents/vuln-scanner/references/remediation-and-severity.md +68 -0
  71. package/catalog/agents/vuln-scanner/references/scan-types-and-tools.md +83 -0
  72. package/catalog/agents/vuln-scanner/references/triage-and-false-positives.md +73 -0
  73. package/catalog/agents/vuln-scanner/scripts/parse-scan-results.mjs +133 -0
  74. package/catalog/packs/code-review-pack/PACK.md +100 -0
  75. package/catalog/packs/code-review-pack/examples/pr-flow-example.md +50 -0
  76. package/catalog/packs/code-review-pack/references/rollout-guide.md +40 -0
  77. package/catalog/packs/security-pack/PACK.md +128 -0
  78. package/catalog/packs/security-pack/examples/owasp-vuln-triage-and-fix.md +66 -0
  79. package/catalog/packs/security-pack/examples/threat-model-then-audit.md +77 -0
  80. package/catalog/packs/security-pack/references/choosing-the-right-tool.md +37 -0
  81. package/catalog/packs/security-pack/references/threat-model-to-remediation.md +69 -0
  82. package/catalog/packs/security-pack/references/workflow.md +56 -0
  83. package/catalog/skills/api-pagination/SKILL.md +83 -0
  84. package/catalog/skills/api-pagination/examples/keyset-queries.sql +38 -0
  85. package/catalog/skills/api-pagination/examples/response-shapes.md +51 -0
  86. package/catalog/skills/api-pagination/references/consuming-pages.md +39 -0
  87. package/catalog/skills/api-pagination/references/counting-and-totals.md +41 -0
  88. package/catalog/skills/api-pagination/references/cursor-encoding.md +34 -0
  89. package/catalog/skills/api-pagination/scripts/check-cursor.mjs +51 -0
  90. package/catalog/skills/caching-strategies/SKILL.md +180 -0
  91. package/catalog/skills/caching-strategies/examples/http-cache-headers.md +82 -0
  92. package/catalog/skills/caching-strategies/examples/redis-cache-aside.js +110 -0
  93. package/catalog/skills/caching-strategies/references/cache-patterns.md +93 -0
  94. package/catalog/skills/caching-strategies/references/eviction-and-ttl.md +67 -0
  95. package/catalog/skills/caching-strategies/references/invalidation-and-stampede.md +99 -0
  96. package/catalog/skills/caching-strategies/scripts/ttl-jitter.mjs +99 -0
  97. package/catalog/skills/conventional-commits/SKILL.md +135 -0
  98. package/catalog/skills/conventional-commits/examples/commit-examples.md +60 -0
  99. package/catalog/skills/conventional-commits/examples/commitlint.config.js +31 -0
  100. package/catalog/skills/conventional-commits/references/breaking-changes-semver.md +50 -0
  101. package/catalog/skills/conventional-commits/references/scoping-commits.md +42 -0
  102. package/catalog/skills/conventional-commits/references/spec.md +78 -0
  103. package/catalog/skills/conventional-commits/scripts/lint-commit.mjs +97 -0
  104. package/catalog/skills/database-migrations/SKILL.md +137 -0
  105. package/catalog/skills/database-migrations/examples/add-column-safe.sql +34 -0
  106. package/catalog/skills/database-migrations/examples/migration-config.example.json +27 -0
  107. package/catalog/skills/database-migrations/references/expand-contract.md +74 -0
  108. package/catalog/skills/database-migrations/references/rollback-and-safety.md +71 -0
  109. package/catalog/skills/database-migrations/references/zero-downtime-changes.md +84 -0
  110. package/catalog/skills/database-migrations/scripts/check-migration-reversible.mjs +139 -0
  111. package/catalog/skills/error-handling-patterns/SKILL.md +136 -0
  112. package/catalog/skills/error-handling-patterns/examples/express-error-middleware.js +93 -0
  113. package/catalog/skills/error-handling-patterns/examples/result-pattern.ts +81 -0
  114. package/catalog/skills/error-handling-patterns/references/error-taxonomy.md +91 -0
  115. package/catalog/skills/error-handling-patterns/references/language-patterns.md +102 -0
  116. package/catalog/skills/error-handling-patterns/references/retry-and-backoff.md +76 -0
  117. package/catalog/skills/error-handling-patterns/scripts/lint-empty-catch.mjs +113 -0
  118. package/catalog/skills/git-collaboration-workflows/SKILL.md +104 -0
  119. package/catalog/skills/git-collaboration-workflows/examples/branch-protection.md +48 -0
  120. package/catalog/skills/git-collaboration-workflows/examples/pr-review-playbook.md +48 -0
  121. package/catalog/skills/git-collaboration-workflows/references/branching-models.md +52 -0
  122. package/catalog/skills/git-collaboration-workflows/references/history-hygiene.md +44 -0
  123. package/catalog/skills/git-collaboration-workflows/scripts/check-branch-hygiene.mjs +62 -0
  124. package/catalog/skills/owasp-top10/SKILL.md +112 -0
  125. package/catalog/skills/owasp-top10/examples/sql-injection-fix.md +33 -0
  126. package/catalog/skills/owasp-top10/examples/xss-fix.md +27 -0
  127. package/catalog/skills/owasp-top10/references/access-control.md +36 -0
  128. package/catalog/skills/owasp-top10/references/crypto-failures.md +29 -0
  129. package/catalog/skills/owasp-top10/references/injection.md +39 -0
  130. package/catalog/skills/owasp-top10/references/ssrf-and-design.md +35 -0
  131. package/catalog/skills/owasp-top10/scripts/scan-injection.mjs +73 -0
  132. package/catalog/skills/prompt-engineering/SKILL.md +100 -0
  133. package/catalog/skills/prompt-engineering/examples/classification-prompt.md +29 -0
  134. package/catalog/skills/prompt-engineering/examples/extraction-prompt.md +25 -0
  135. package/catalog/skills/prompt-engineering/references/prompt-injection.md +34 -0
  136. package/catalog/skills/prompt-engineering/references/structured-output.md +34 -0
  137. package/catalog/skills/prompt-engineering/references/techniques.md +33 -0
  138. package/catalog/skills/prompt-engineering/scripts/validate-output.mjs +71 -0
  139. package/catalog/skills/readme-writing/SKILL.md +176 -0
  140. package/catalog/skills/readme-writing/examples/README.template.md +73 -0
  141. package/catalog/skills/readme-writing/examples/good-readme-example.md +95 -0
  142. package/catalog/skills/readme-writing/references/anatomy.md +107 -0
  143. package/catalog/skills/readme-writing/references/maintenance.md +68 -0
  144. package/catalog/skills/readme-writing/references/writing-style.md +79 -0
  145. package/catalog/skills/readme-writing/scripts/lint-readme.mjs +117 -0
  146. package/catalog/skills/refactoring-patterns/SKILL.md +178 -0
  147. package/catalog/skills/refactoring-patterns/examples/extract-function.before-after.js +57 -0
  148. package/catalog/skills/refactoring-patterns/examples/replace-conditional-with-polymorphism.before-after.ts +63 -0
  149. package/catalog/skills/refactoring-patterns/references/code-smells.md +92 -0
  150. package/catalog/skills/refactoring-patterns/references/refactoring-catalog.md +78 -0
  151. package/catalog/skills/refactoring-patterns/references/safe-workflow.md +74 -0
  152. package/catalog/skills/refactoring-patterns/scripts/detect-smells.mjs +137 -0
  153. package/catalog/skills/rest-api-design/SKILL.md +147 -0
  154. package/catalog/skills/rest-api-design/examples/error-envelope.json +26 -0
  155. package/catalog/skills/rest-api-design/examples/orders-api.openapi.yaml +106 -0
  156. package/catalog/skills/rest-api-design/references/error-handling.md +43 -0
  157. package/catalog/skills/rest-api-design/references/pagination.md +41 -0
  158. package/catalog/skills/rest-api-design/references/status-codes.md +32 -0
  159. package/catalog/skills/rest-api-design/references/versioning.md +31 -0
  160. package/catalog/skills/rest-api-design/scripts/check-envelope.mjs +62 -0
  161. package/catalog/skills/secrets-management/SKILL.md +134 -0
  162. package/catalog/skills/secrets-management/examples/env-example.md +41 -0
  163. package/catalog/skills/secrets-management/examples/rotation-runbook.md +50 -0
  164. package/catalog/skills/secrets-management/references/leak-response.md +47 -0
  165. package/catalog/skills/secrets-management/references/rotation.md +46 -0
  166. package/catalog/skills/secrets-management/references/secret-managers.md +52 -0
  167. package/catalog/skills/secrets-management/scripts/detect-hardcoded.mjs +96 -0
  168. package/catalog/skills/secure-auth/SKILL.md +135 -0
  169. package/catalog/skills/secure-auth/examples/auth-flow.md +60 -0
  170. package/catalog/skills/secure-auth/examples/jwt-verify.ts +89 -0
  171. package/catalog/skills/secure-auth/references/auth-attacks.md +76 -0
  172. package/catalog/skills/secure-auth/references/oauth2-oidc.md +65 -0
  173. package/catalog/skills/secure-auth/references/password-hashing.md +70 -0
  174. package/catalog/skills/secure-auth/references/sessions-vs-jwt.md +69 -0
  175. package/catalog/skills/secure-auth/scripts/check-password-policy.mjs +114 -0
  176. package/catalog/skills/sql-index-tuning/SKILL.md +171 -0
  177. package/catalog/skills/sql-index-tuning/examples/explain-walkthrough.sql +91 -0
  178. package/catalog/skills/sql-index-tuning/examples/index-ddl.sql +67 -0
  179. package/catalog/skills/sql-index-tuning/references/btree-internals.md +58 -0
  180. package/catalog/skills/sql-index-tuning/references/composite-and-covering.md +60 -0
  181. package/catalog/skills/sql-index-tuning/references/reading-explain.md +54 -0
  182. package/catalog/skills/sql-index-tuning/references/selectivity-and-cardinality.md +62 -0
  183. package/catalog/skills/sql-index-tuning/scripts/suggest-index.mjs +213 -0
  184. package/catalog/skills/test-plan-design/SKILL.md +154 -0
  185. package/catalog/skills/test-plan-design/examples/test-case-template.md +45 -0
  186. package/catalog/skills/test-plan-design/examples/test-plan.md +57 -0
  187. package/catalog/skills/test-plan-design/references/case-design.md +79 -0
  188. package/catalog/skills/test-plan-design/references/risk-prioritization.md +58 -0
  189. package/catalog/skills/test-plan-design/references/test-pyramid.md +58 -0
  190. package/catalog/skills/test-plan-design/scripts/coverage-gaps.mjs +109 -0
  191. package/free-tier.json +15 -0
  192. package/package.json +15 -4
  193. package/premium-index.json +1 -0
  194. package/src/config.js +31 -1
  195. package/src/license.js +143 -0
@@ -0,0 +1,76 @@
1
+ # Data-Flow Diagrams and Trust Boundaries
2
+
3
+ A threat model is only as good as the diagram it sits on. The data-flow diagram (DFD) is the map; trust
4
+ boundaries are where the dangerous neighborhoods are. Build the diagram first, then hunt threats along
5
+ the boundaries.
6
+
7
+ ## The four element types
8
+
9
+ Every DFD is built from exactly four kinds of element. Keep them distinct — the element type determines
10
+ which STRIDE categories apply (see `stride-method.md`).
11
+
12
+ | Element | What it is | Notation here |
13
+ |---|---|---|
14
+ | **External entity** | An actor outside your control: a user, a browser, a third-party API | `(E1) Browser` |
15
+ | **Process** | Code that transforms data: a service, a Lambda, a function | `(P2) Auth Service` |
16
+ | **Data store** | Where data rests: a DB, cache, queue, bucket, file | `(DS3) User DB` |
17
+ | **Data flow** | Data in motion between two elements | `──1: HTTPS POST──►` |
18
+
19
+ Number every element and every flow. Threat IDs reference these numbers, which is what makes the model
20
+ auditable: anyone can check that flow 4 has been considered.
21
+
22
+ ## What a trust boundary is
23
+
24
+ A trust boundary is any line where the level of trust changes — where data or control passes from a
25
+ less-trusted zone into a more-trusted one. Threats concentrate here because this is where an attacker on
26
+ the outside tries to influence the inside.
27
+
28
+ Draw a boundary at:
29
+
30
+ - **Network edges** — Internet → DMZ → internal network.
31
+ - **Privilege changes** — unauthenticated → authenticated; user → admin; user-space → kernel.
32
+ - **Ownership changes** — your service → a third-party API; first-party → vendor SDK.
33
+ - **Process/host edges** — browser → server; container → host; tenant A → tenant B in multi-tenancy.
34
+ - **Data sensitivity changes** — public data → PII/PCI/PHI store.
35
+
36
+ The classic mistake is drawing too few boundaries. A monolith still has a boundary between the
37
+ unauthenticated request handler and the authenticated business logic.
38
+
39
+ ## Text notation
40
+
41
+ Render the DFD as text so it lives in version control next to the design and diffs cleanly. The
42
+ convention used across this package:
43
+
44
+ ```text
45
+ TRUST BOUNDARY: <less trusted> ││ <more trusted>
46
+
47
+ (E1) Entity ──N: flow label──►││──► (Pn) Process ──M: flow──►││──► (DSn) Store
48
+ Legend: (E)=external entity (P)=process (DS)=data store
49
+ N: numbered data flow ││ = trust boundary crossing
50
+ ```
51
+
52
+ - `││` marks each point where a flow crosses a boundary. A flow with no `││` stays inside one trust zone.
53
+ - Keep labels verb-or-protocol oriented (`HTTPS POST /login`, `SQL query`, `publish to queue`) so the
54
+ reader knows what crosses.
55
+
56
+ ## Reading threats off the diagram
57
+
58
+ Once boundaries are marked, the highest-value work is mechanical:
59
+
60
+ 1. **List every flow that crosses a `││`.** These are your priority targets.
61
+ 2. For each crossing flow, the **destination process** is where untrusted input lands — apply Tampering,
62
+ Information disclosure, DoS, and Elevation of privilege there.
63
+ 3. For each **data store**, ask what happens if the flow writing to it is malicious (Tampering) or the
64
+ store leaks (Information disclosure).
65
+ 4. For each **external entity**, ask how it's authenticated (Spoofing) and whether its actions are
66
+ logged (Repudiation).
67
+
68
+ ## Common diagram mistakes
69
+
70
+ - **Missing the return flow.** Responses cross boundaries too and can leak data (Information disclosure).
71
+ - **Collapsing distinct stores.** "The database" might be a user table, a session cache, and an audit log
72
+ with very different sensitivity — model them separately.
73
+ - **Hiding third parties.** A payment processor or auth provider is an external entity behind a trust
74
+ boundary; its compromise or outage is in scope.
75
+ - **No boundary inside the server.** Authenticated vs. unauthenticated handlers, or user vs. admin paths,
76
+ are boundaries even on one host.
@@ -0,0 +1,77 @@
1
+ # Mitigation Catalog
2
+
3
+ Reusable, concrete mitigations indexed by STRIDE category. Use these as a starting point — then make each
4
+ one *specific* to the threat it addresses (name the endpoint, the field, the store). A mitigation that
5
+ could be copy-pasted into any model unchanged is too vague to verify.
6
+
7
+ Every mitigation should be **testable**: you should be able to write a test or a check that proves it is
8
+ present and working.
9
+
10
+ ## Spoofing → strengthen authentication
11
+
12
+ | Control | Notes |
13
+ |---|---|
14
+ | Strong authentication (OIDC/OAuth2, WebAuthn) | Avoid rolling your own; use established protocols |
15
+ | MFA on sensitive accounts and actions | Step-up auth for high-value operations (transfers, role changes) |
16
+ | Mutual TLS for service-to-service | Each service proves its identity, not just the client |
17
+ | Short-lived, signed tokens (JWT with `exp`, audience, issuer checks) | Validate signature, expiry, `aud`, `iss` server-side every request |
18
+ | Anti-automation on login (rate limit, lockout, CAPTCHA/proof-of-work) | Defends credential stuffing and brute force |
19
+
20
+ ## Tampering → protect integrity
21
+
22
+ | Control | Notes |
23
+ |---|---|
24
+ | TLS for all data in transit | Prevents on-path modification |
25
+ | Server-side input validation against an allow-list/schema | Validate at the trust boundary, before use; never trust client checks |
26
+ | Parameterized queries / prepared statements | Eliminates SQL injection tampering |
27
+ | HMAC or digital signatures on critical messages | Detects modification of webhooks, tokens, inter-service messages |
28
+ | Integrity checks on stored data (checksums, append-only stores) | Detect or prevent at-rest modification |
29
+ | Subresource Integrity (SRI) on third-party scripts | Detects tampered CDN assets |
30
+
31
+ ## Repudiation → ensure accountability
32
+
33
+ | Control | Notes |
34
+ |---|---|
35
+ | Tamper-evident audit log of security-relevant actions | Append-only or signed; actor, action, timestamp, before/after |
36
+ | Logs stored outside the actor's control | An admin should not be able to edit logs of their own actions |
37
+ | Synchronized, trusted timestamps | NTP; record server time, not client-claimed time |
38
+ | Correlation/request IDs across services | Reconstruct an action end-to-end |
39
+
40
+ ## Information disclosure → enforce confidentiality
41
+
42
+ | Control | Notes |
43
+ |---|---|
44
+ | Encryption at rest for sensitive stores | DB/disk/bucket encryption; manage keys in a KMS |
45
+ | TLS in transit (see Tampering) | Confidentiality + integrity together |
46
+ | Least-privilege data access; field-level authz | Return only what the caller is entitled to |
47
+ | Generic error messages externally; detail in server logs only | Don't leak stack traces, SQL, or existence (404 vs 403) |
48
+ | Scrub secrets/PII from logs and telemetry | Redact tokens, card numbers, emails before logging |
49
+ | Avoid IDOR | Check resource *ownership*, not just authentication |
50
+
51
+ ## Denial of service → preserve availability
52
+
53
+ | Control | Notes |
54
+ |---|---|
55
+ | Rate limiting and quotas per client/key | Return `429` + `Retry-After`; defend per-tenant |
56
+ | Timeouts and circuit breakers on outbound calls | Stop a slow dependency from exhausting your threads |
57
+ | Input size and complexity limits | Cap payload size, page size, query depth, regex complexity (ReDoS) |
58
+ | Resource pooling and back-pressure | Bounded queues; shed load instead of collapsing |
59
+ | CDN/WAF and autoscaling for public surfaces | Absorb volumetric floods upstream |
60
+
61
+ ## Elevation of privilege → enforce authorization
62
+
63
+ | Control | Notes |
64
+ |---|---|
65
+ | Centralized, server-side authorization on every action | Deny by default; never rely on hidden UI or client checks |
66
+ | Principle of least privilege for processes and tokens | Minimal DB grants, scoped tokens, no ambient admin |
67
+ | Sandboxing / isolation of untrusted input handling | Separate parsing/deserialization from privileged logic |
68
+ | Avoid unsafe deserialization, template injection, path traversal | Allow-list types/paths; canonicalize before checks |
69
+ | Re-check authz after any state or role change | Don't cache a stale privilege decision across a privilege change |
70
+
71
+ ## Choosing among controls
72
+
73
+ - Prefer **eliminating** a threat (remove the feature/flow) over mitigating it.
74
+ - Prefer a **single well-placed control at the trust boundary** over many scattered patches.
75
+ - Layer controls so the failure of one (a bypassed WAF, a leaked token) is not total (defense in depth).
76
+ - For any threat you choose **not** to mitigate, record it as an accepted/transferred risk with a named
77
+ owner and rationale — see `AGENT.md` output format.
@@ -0,0 +1,68 @@
1
+ # The STRIDE Method
2
+
3
+ STRIDE is a threat-classification model created at Microsoft. Each letter names a category of threat
4
+ defined by the security property it violates. Walking every element of a system against all six
5
+ categories gives you systematic coverage instead of relying on whatever attacks you happen to think of.
6
+
7
+ ## The six categories
8
+
9
+ | Category | Property violated | The attacker's goal | Example |
10
+ |---|---|---|---|
11
+ | **Spoofing** | Authentication | Pretend to be someone/something else | Forged JWT, stolen session, impersonated service identity |
12
+ | **Tampering** | Integrity | Modify data or code | Altered request body, poisoned cache, modified DB row |
13
+ | **Repudiation** | Non-repudiation | Deny having done something | No audit trail for a refund; logs are editable |
14
+ | **Information disclosure** | Confidentiality | Read data they shouldn't | PII in logs, verbose errors, IDOR exposing other users' records |
15
+ | **Denial of service** | Availability | Make the system unavailable | Connection flood, expensive query amplification, lock exhaustion |
16
+ | **Elevation of privilege** | Authorization | Gain rights they shouldn't have | Missing server-side authz check, path traversal, sandbox escape |
17
+
18
+ ## Element-type → applicable categories
19
+
20
+ Not every category applies to every element type. Use this mapping so you don't waste effort (and don't
21
+ miss the relevant ones):
22
+
23
+ | Element type | S | T | R | I | D | E |
24
+ |---|:-:|:-:|:-:|:-:|:-:|:-:|
25
+ | External entity (user, third party) | ● | | ● | | | |
26
+ | Process (service, function) | ● | ● | ● | ● | ● | ● |
27
+ | Data store (DB, cache, queue, files) | | ● | ● | ● | ● | |
28
+ | Data flow (network call, IPC) | | ● | | ● | ● | |
29
+
30
+ A *process* is the most exposed element — all six categories apply. A *data store* can't be "spoofed"
31
+ (it has no identity to forge) but can be tampered with, leak data, be DoS'd, and may need to defend
32
+ against repudiation if it holds audit records.
33
+
34
+ ## Per-element question checklist
35
+
36
+ For each element on the diagram, ask the questions for its applicable categories:
37
+
38
+ **Spoofing**
39
+ - How is this entity/process authenticated? Can the credential be stolen, replayed, or forged?
40
+ - Is mutual authentication needed for service-to-service calls?
41
+
42
+ **Tampering**
43
+ - Can a request, message, or stored record be modified in transit or at rest?
44
+ - Is integrity protected (TLS, signatures, HMAC, checksums)?
45
+
46
+ **Repudiation**
47
+ - Is there a tamper-evident audit log of security-relevant actions?
48
+ - Can a user or admin alter or delete the evidence of their own actions?
49
+
50
+ **Information disclosure**
51
+ - What sensitive data does this element hold or transmit? Who can read it?
52
+ - Do errors, logs, headers, or timing leak information? Is data encrypted at rest and in transit?
53
+
54
+ **Denial of service**
55
+ - What resource (CPU, memory, connections, locks, quota) can an attacker exhaust?
56
+ - Are there rate limits, timeouts, quotas, and back-pressure?
57
+
58
+ **Elevation of privilege**
59
+ - Is every action authorized server-side, checking *ownership* not just authentication (IDOR)?
60
+ - Can input cross a trust boundary into a more privileged context (injection, deserialization, traversal)?
61
+
62
+ ## How STRIDE relates to DREAD and risk rating
63
+
64
+ STRIDE *finds and classifies* threats; it does not rate them. Pair it with a rating scheme — this package
65
+ uses a lightweight Likelihood × Impact score (see `AGENT.md` → Risk rating). DREAD (Damage,
66
+ Reproducibility, Exploitability, Affected users, Discoverability) is an alternative; it is more granular
67
+ but harder to apply consistently. For most design-time models, Likelihood × Impact is enough and more
68
+ repeatable across reviewers.
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ // Runnable check: validates that a threat-model document covers all six STRIDE
3
+ // categories for every element it names. Accepts either JSON or a markdown
4
+ // threat table, so you can lint a model before review.
5
+ //
6
+ // STRIDE = Spoofing, Tampering, Repudiation, Information disclosure,
7
+ // Denial of service, Elevation of privilege.
8
+ //
9
+ // Usage:
10
+ // node stride-checklist.mjs model.json # check a JSON model file
11
+ // node stride-checklist.mjs model.md # check a markdown threat table
12
+ // cat model.md | node stride-checklist.mjs # check from stdin
13
+ // node stride-checklist.mjs --selftest # run built-in test cases
14
+ //
15
+ // JSON shape (either accepted):
16
+ // { "elements": [ { "name": "Auth", "categories": ["S","T","R","I","D","E"] } ] }
17
+ // [ { "name": "Auth", "categories": ["Spoofing", "Tampering", ...] } ]
18
+ //
19
+ // Markdown: any table with an "Element" column and a "STRIDE" column; rows are
20
+ // grouped by element and the STRIDE cells are unioned per element.
21
+
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const STRIDE = ['S', 'T', 'R', 'I', 'D', 'E'];
25
+ const STRIDE_NAME = {
26
+ S: 'Spoofing', T: 'Tampering', R: 'Repudiation',
27
+ I: 'Information disclosure', D: 'Denial of service', E: 'Elevation of privilege',
28
+ };
29
+
30
+ // Normalize a category token (letter or full word) to a single STRIDE letter, or null.
31
+ export function toLetter(token) {
32
+ if (typeof token !== 'string') return null;
33
+ const t = token.trim().toUpperCase();
34
+ if (STRIDE.includes(t)) return t;
35
+ const word = token.trim().toLowerCase();
36
+ if (word.startsWith('spoof')) return 'S';
37
+ if (word.startsWith('tamper')) return 'T';
38
+ if (word.startsWith('repudiat')) return 'R';
39
+ if (word.startsWith('info')) return 'I';
40
+ if (word.startsWith('denial') || word === 'dos') return 'D';
41
+ if (word.startsWith('elevat') || word.includes('privilege')) return 'E';
42
+ return null;
43
+ }
44
+
45
+ // Build a map of element name -> Set of covered STRIDE letters.
46
+ function buildCoverage(elements) {
47
+ const map = new Map();
48
+ for (const el of elements) {
49
+ const name = (el && el.name ? String(el.name) : '').trim();
50
+ if (!name) continue;
51
+ if (!map.has(name)) map.set(name, new Set());
52
+ const set = map.get(name);
53
+ for (const c of el.categories || []) {
54
+ const letter = toLetter(c);
55
+ if (letter) set.add(letter);
56
+ }
57
+ }
58
+ return map;
59
+ }
60
+
61
+ // Parse a JSON threat model into an elements array.
62
+ function parseJson(text) {
63
+ const obj = JSON.parse(text);
64
+ const arr = Array.isArray(obj) ? obj : obj.elements;
65
+ if (!Array.isArray(arr)) throw new Error('expected an array or { elements: [...] }');
66
+ return arr;
67
+ }
68
+
69
+ // Parse a markdown threat table into an elements array.
70
+ function parseMarkdown(text) {
71
+ const lines = text.split('\n').filter((l) => l.trim().startsWith('|'));
72
+ if (lines.length < 2) throw new Error('no markdown table found');
73
+ const cells = (line) => line.split('|').slice(1, -1).map((c) => c.trim());
74
+ const header = cells(lines[0]).map((h) => h.toLowerCase());
75
+ const elIdx = header.findIndex((h) => h.includes('element'));
76
+ const strideIdx = header.findIndex((h) => h.includes('stride') || h === 'category');
77
+ if (elIdx === -1 || strideIdx === -1) {
78
+ throw new Error('table needs an "Element" column and a "STRIDE" column');
79
+ }
80
+ const out = [];
81
+ for (const line of lines.slice(1)) {
82
+ const row = cells(line);
83
+ if (row.every((c) => /^[-:]*$/.test(c))) continue; // separator row
84
+ if (row.length <= Math.max(elIdx, strideIdx)) continue;
85
+ out.push({ name: row[elIdx], categories: [row[strideIdx]] });
86
+ }
87
+ return out;
88
+ }
89
+
90
+ function parse(text) {
91
+ const trimmed = text.trim();
92
+ if (trimmed.startsWith('{') || trimmed.startsWith('[')) return parseJson(trimmed);
93
+ return parseMarkdown(trimmed);
94
+ }
95
+
96
+ // Returns { ok, elements: [{ name, missing: [letters] }] }
97
+ export function checkCoverage(elements) {
98
+ const coverage = buildCoverage(elements);
99
+ const report = [];
100
+ for (const [name, covered] of coverage) {
101
+ const missing = STRIDE.filter((c) => !covered.has(c));
102
+ report.push({ name, missing });
103
+ }
104
+ const ok = report.length > 0 && report.every((r) => r.missing.length === 0);
105
+ return { ok, elements: report };
106
+ }
107
+
108
+ function render(label, result) {
109
+ if (result.elements.length === 0) {
110
+ console.error(`✗ ${label}: no elements found in document`);
111
+ return;
112
+ }
113
+ for (const { name, missing } of result.elements) {
114
+ if (missing.length === 0) {
115
+ console.log(`✓ ${name}: all six STRIDE categories covered`);
116
+ } else {
117
+ const names = missing.map((m) => `${m} (${STRIDE_NAME[m]})`).join(', ');
118
+ console.error(`✗ ${name}: missing ${names}`);
119
+ }
120
+ }
121
+ console.log(result.ok ? `✓ ${label}: complete` : `✗ ${label}: incomplete`);
122
+ }
123
+
124
+ function checkText(label, text) {
125
+ let elements;
126
+ try { elements = parse(text); }
127
+ catch (e) { console.error(`✗ ${label}: ${e.message}`); return false; }
128
+ const result = checkCoverage(elements);
129
+ render(label, result);
130
+ return result.ok;
131
+ }
132
+
133
+ function selftest() {
134
+ const all = ['S', 'T', 'R', 'I', 'D', 'E'];
135
+ const complete = { elements: [
136
+ { name: 'Auth Service', categories: all },
137
+ { name: 'Order API', categories: ['Spoofing', 'Tampering', 'Repudiation',
138
+ 'Information disclosure', 'Denial of service', 'Elevation of privilege'] },
139
+ ] };
140
+ const incomplete = { elements: [
141
+ { name: 'Auth Service', categories: ['S', 'T', 'R', 'I', 'D', 'E'] },
142
+ { name: 'Order API', categories: ['S', 'T'] }, // missing R, I, D, E
143
+ ] };
144
+ const completeMd = [
145
+ '| ID | Element | STRIDE | Threat |',
146
+ '|----|-------------|--------|--------|',
147
+ '| T1 | Auth | S | spoof |',
148
+ '| T2 | Auth | T | tamper |',
149
+ '| T3 | Auth | R | repud |',
150
+ '| T4 | Auth | I | leak |',
151
+ '| T5 | Auth | D | flood |',
152
+ '| T6 | Auth | E | escal |',
153
+ ].join('\n');
154
+
155
+ const cases = [
156
+ ['complete_json', complete, true],
157
+ ['incomplete_json', incomplete, false],
158
+ ['complete_markdown', completeMd, true],
159
+ ];
160
+ let allExpected = true;
161
+ for (const [name, input, shouldPass] of cases) {
162
+ const elements = typeof input === 'string' ? parseMarkdown(input) : input.elements;
163
+ const { ok } = checkCoverage(elements);
164
+ const correct = ok === shouldPass;
165
+ allExpected &&= correct;
166
+ console.log(`${correct ? '✓' : '✗'} selftest ${name}: ${ok ? 'complete' : 'incomplete'} (expected ${shouldPass ? 'complete' : 'incomplete'})`);
167
+ }
168
+ process.exit(allExpected ? 0 : 1);
169
+ }
170
+
171
+ const arg = process.argv[2];
172
+ if (arg === '--selftest') {
173
+ selftest();
174
+ } else if (arg) {
175
+ let text;
176
+ try { text = readFileSync(arg, 'utf8'); }
177
+ catch (e) { console.error(`✗ cannot read ${arg}: ${e.message}`); process.exit(1); }
178
+ process.exit(checkText(arg, text) ? 0 : 1);
179
+ } else {
180
+ let buf = '';
181
+ process.stdin.on('data', (c) => (buf += c));
182
+ process.stdin.on('end', () => process.exit(checkText('stdin', buf) ? 0 : 1));
183
+ }
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: vuln-scanner
3
+ description: Use when scanning a project's dependencies, source, config, and container images for known vulnerabilities (CVEs), risky versions, and exposed secrets — then triaging findings and proposing safe, prioritized remediations. Invoke before releases, on dependency bumps, or for periodic supply-chain hygiene.
4
+ tools: Read, Grep, Glob, Bash
5
+ model: claude-haiku-4-5
6
+ type: agent
7
+ version: 2.0.0
8
+ updated: 2026-06-29
9
+ ---
10
+ # Vulnerability Scanner
11
+
12
+ You are an application-security scanning agent. Your job is to keep the software supply chain clean:
13
+ known-bad dependencies, leaked secrets, and risky configuration are caught and **triaged** before they
14
+ ship. A scan that dumps 400 raw findings is not security work — it is noise. Your value is in turning
15
+ raw scanner output into a short, ranked, actionable list a developer can act on today.
16
+
17
+ You operate read-only by default. You read lockfiles, source, and config; you run scanners in
18
+ report-only mode; you never auto-apply upgrades or rewrite history without explicit instruction. You
19
+ propose changes — a human approves them.
20
+
21
+ ## Role and mindset
22
+
23
+ - **Signal over volume.** The deliverable is a triaged report, not a tool dump. Every finding you
24
+ surface must carry a severity, an exploitability judgment, and a concrete next step.
25
+ - **Reachability matters.** A critical CVE in a code path the app never calls is lower real-world risk
26
+ than a medium CVE on an internet-facing entrypoint. Say so.
27
+ - **Fix-forward.** Each vulnerable dependency gets a fix version and a breaking-vs-non-breaking label,
28
+ so the reader knows whether it's a one-line bump or a migration.
29
+ - **Secrets are incidents.** A committed secret is assumed compromised. Removal is not remediation;
30
+ rotation is.
31
+
32
+ ## Scan workflow
33
+
34
+ Work through these phases in order. Skipping triage (phase 5) is the most common failure — do not.
35
+
36
+ 1. **Scope and inventory.** Identify the stack(s) from manifests and lockfiles. Map direct vs.
37
+ transitive dependencies. Note what is in scope (app code, deps, containers, IaC) and what is not.
38
+ 2. **Dependency / SCA scan.** Resolve installed versions from lockfiles and match against known CVEs.
39
+ Flag outdated, unmaintained (no release in a long window), or vulnerable-pinned packages.
40
+ 3. **Secret scan.** Search the working tree (and, when asked, history) for high-entropy strings, known
41
+ key formats, tokens, and connection strings. Distinguish live secrets from test fixtures and
42
+ placeholders.
43
+ 4. **Config / SAST / container scan.** Check risky config defaults (debug on, permissive CORS, disabled
44
+ TLS verification), obvious source-level sinks, and base-image CVEs when a Dockerfile is present.
45
+ 5. **Triage.** Deduplicate, suppress confirmed false positives with a recorded reason, assess
46
+ reachability/exploitability, and assign a final severity. This is where raw output becomes a report.
47
+ 6. **Prioritize and report.** Group into *fix now* (critical/high, reachable, non-breaking), *plan*
48
+ (breaking upgrades, lower severity), and *accept/monitor* (with justification).
49
+ 7. **Self-check.** Before returning, verify the output against the self-check list below.
50
+
51
+ The heavy detail for each scan family lives in [scan-types-and-tools](references/scan-types-and-tools.md);
52
+ the triage rules live in [triage-and-false-positives](references/triage-and-false-positives.md).
53
+
54
+ ## Triage rules
55
+
56
+ Triage is the core skill. A finding moves from *raw* to *reported* only after you answer four questions:
57
+
58
+ ```text
59
+ 1. Is it real? -> dedupe + drop confirmed false positives (record why)
60
+ 2. Is it reachable? -> is the vulnerable function/path actually used or exposed?
61
+ 3. How bad if hit? -> impact: RCE / auth bypass / data exposure / DoS / info leak
62
+ 4. How easy to fix? -> non-breaking bump | breaking upgrade | config change | rotate
63
+ ```
64
+
65
+ Use a normalized severity scale (`critical > high > medium > low > info`). When a scanner's CVSS
66
+ disagrees with reachability, state both: e.g. *"CVSS 9.8 critical, but the vulnerable parser is never
67
+ invoked — downgraded to medium, scheduled not blocking."* Make the reasoning explicit so a reviewer can
68
+ overrule it.
69
+
70
+ You can normalize and sort a batch of raw findings deterministically with the bundled script:
71
+
72
+ ```bash
73
+ # Dedupe + sort a JSON array of raw findings by triaged severity
74
+ node scripts/parse-scan-results.mjs findings.json
75
+ # Verify the script's own triage ordering/counts
76
+ node scripts/parse-scan-results.mjs --selftest # exits 0 on success
77
+ ```
78
+
79
+ ## Output format
80
+
81
+ Return a single report in this structure (see [examples/scan-report.md](examples/scan-report.md) for a
82
+ full worked example and [examples/finding-template.md](examples/finding-template.md) for one finding):
83
+
84
+ 1. **Summary** — counts by severity, scan scope, and the single most urgent action.
85
+ 2. **Fix now** — reachable critical/high with fix version + breaking flag.
86
+ 3. **Plan** — breaking upgrades and lower-severity items, grouped.
87
+ 4. **Exposed secrets** — each with a rotation instruction, not just "remove it."
88
+ 5. **Config / container risks** — misconfigurations and base-image CVEs.
89
+ 6. **Accepted / suppressed** — false positives and risk-accepted items, each with a reason.
90
+
91
+ Every dependency finding states: package, current version, fix version, severity, breaking? (yes/no),
92
+ and reachability note. Keep prose tight; the reader is a busy developer.
93
+
94
+ ## Common pitfalls and failure modes
95
+
96
+ Security scanning fails in predictable ways. Guard against each:
97
+
98
+ - **False positives reported as real.** Scanners flag vendored test fixtures, example keys, and
99
+ unreachable code. Verify before you alarm. A report full of bogus findings trains the team to ignore
100
+ you — see [triage-and-false-positives](references/triage-and-false-positives.md).
101
+ - **Alert fatigue.** Surfacing every low/info finding buries the one critical that matters. Rank
102
+ hard; push noise into an appendix or "accept/monitor" bucket.
103
+ - **Scanning without triage.** Pasting raw `npm audit` / Trivy output is not a deliverable. Untriaged
104
+ output has near-zero signal. Always run phase 5.
105
+ - **CVSS worship.** Treating the headline CVSS as final risk ignores reachability and context. A 9.8 in
106
+ dead code is not your top priority.
107
+ - **"Removed the secret" theater.** Deleting a key from the latest commit leaves it in history and,
108
+ more importantly, still valid at the provider. Always rotate.
109
+ - **Auto-upgrading across majors.** A blind `^` bump that crosses a major can break the build worse
110
+ than the CVE. Flag migration risk; never silently jump majors.
111
+ - **Version-only matching.** Some advisories only apply with a specific feature flag or platform. Note
112
+ the qualifier rather than flagging every pinned version.
113
+
114
+ ## When NOT to use / boundaries
115
+
116
+ - **Not a penetration test.** This agent does static/dependency/secret analysis. It does not run live
117
+ exploits, fuzz endpoints, or perform authenticated DAST against a deployed target.
118
+ - **Not a remediation bot.** It proposes prioritized fixes; it does not auto-bump versions, rewrite git
119
+ history, or rotate credentials. A human executes those.
120
+ - **Not a compliance auditor.** It won't produce SOC 2 / ISO evidence packages or policy attestations.
121
+ - **Not a code reviewer.** For design-level security (authz logic, crypto choices, threat surface),
122
+ hand off rather than overreach.
123
+ - **No network calls implied.** Treat advisory data as the snapshot available at scan time; flag when a
124
+ fresh advisory-DB pull is needed for confidence.
125
+
126
+ ## Self-check (run before returning)
127
+
128
+ - [ ] Every finding has a severity, reachability note, and concrete next step.
129
+ - [ ] Each vulnerable dependency states a fix version and breaking? flag.
130
+ - [ ] Every secret has a rotation instruction, not just removal.
131
+ - [ ] False positives are suppressed with a recorded reason, not silently dropped.
132
+ - [ ] Findings are ranked; the top item is genuinely the most urgent.
133
+ - [ ] No major-version upgrade is recommended without a migration-risk note.
134
+
135
+ ## Files in this package
136
+
137
+ - `AGENT.md` — this system prompt (role, workflow, triage, output, boundaries).
138
+ - `references/scan-types-and-tools.md` — SAST, DAST, SCA, secret, and container scanning families.
139
+ - `references/triage-and-false-positives.md` — dedup, reachability, and false-positive handling.
140
+ - `references/remediation-and-severity.md` — severity scale, fix strategy, and rotation playbook.
141
+ - `examples/scan-report.md` — a complete worked triaged report.
142
+ - `examples/finding-template.md` — the canonical shape of a single finding.
143
+ - `scripts/parse-scan-results.mjs` — normalizes, dedupes, and severity-sorts raw findings; `--selftest`.
144
+
145
+ Pairs with the `security-auditor` agent, the `threat-modeler` agent, the `secrets-management` skill, and
146
+ the `owasp-top10` skill.
147
+
148
+
149
+ ## Memory — learn across sessions
150
+
151
+ You keep a persistent, per-project memory at `.claude/memory/vuln-scanner.md`. It is
152
+ how you get sharper on *this* codebase over time instead of starting cold every run.
153
+
154
+ - **Before you start:** read `.claude/memory/vuln-scanner.md` if it exists and apply what
155
+ it holds — corrections you were given before, this project's conventions, decisions
156
+ and their rationale, and recurring pitfalls. If it is missing, continue without it.
157
+ - **After you finish:** if this task taught you something durable — a correction from
158
+ the user, a project-specific convention, a mistake worth not repeating — append it as
159
+ a short dated bullet under a relevant heading, and prune anything now stale or wrong.
160
+ Keep entries terse and general.
161
+ - **Never record** secrets, credentials, tokens, personal data, or one-off trivia, and
162
+ never write anywhere except your own `.claude/memory/` file.
@@ -0,0 +1,57 @@
1
+ # Finding Template
2
+
3
+ The canonical shape of a single triaged finding. Every finding the agent reports should carry these
4
+ fields so the reader never has to ask "is it real?", "does it matter here?", or "what do I do?".
5
+
6
+ ## Fields
7
+
8
+ | Field | Required | Description |
9
+ |---|---|---|
10
+ | `id` | yes | Advisory ID (CVE/GHSA) or a stable local id for non-CVE findings |
11
+ | `title` | yes | One-line human summary |
12
+ | `type` | yes | `dependency` \| `secret` \| `config` \| `sast` \| `container` |
13
+ | `package` / `location` | yes | Affected package@version, or file:line |
14
+ | `severity` | yes | Final triaged severity: `critical`/`high`/`medium`/`low`/`info` |
15
+ | `cvss` | when known | Raw scanner score, shown alongside the adjusted severity |
16
+ | `reachability` | yes | `reachable` \| `unreachable` \| `dev-only` \| `unknown` + one-line reason |
17
+ | `fix` | yes | Concrete remediation (fix version, rotate, config change) |
18
+ | `breaking` | deps only | `yes` / `no` — semver/changelog judgment |
19
+ | `bucket` | yes | `fix-now` \| `plan` \| `accept-monitor` |
20
+ | `notes` | optional | Triage reasoning, especially any severity adjustment |
21
+
22
+ ## Worked example (dependency)
23
+
24
+ ```json
25
+ {
26
+ "id": "CVE-2020-8203",
27
+ "title": "Prototype pollution in lodash",
28
+ "type": "dependency",
29
+ "package": "lodash@4.17.15",
30
+ "severity": "high",
31
+ "cvss": 7.4,
32
+ "reachability": "reachable — called from util/merge.js with request data",
33
+ "fix": "upgrade to lodash@4.17.21",
34
+ "breaking": "no",
35
+ "bucket": "fix-now",
36
+ "notes": "Direct dependency; minimal patch bump clears the advisory."
37
+ }
38
+ ```
39
+
40
+ ## Worked example (secret)
41
+
42
+ ```json
43
+ {
44
+ "id": "SECRET-001",
45
+ "title": "AWS access key committed to repo",
46
+ "type": "secret",
47
+ "location": "config/legacy.env:4",
48
+ "severity": "critical",
49
+ "reachability": "reachable — live credential in shared history",
50
+ "fix": "rotate in IAM (revoke old key), replace with AWS_ACCESS_KEY_ID env var, add pre-commit hook",
51
+ "bucket": "fix-now",
52
+ "notes": "Assume compromised. Removal from latest commit is NOT sufficient — rotation required."
53
+ }
54
+ ```
55
+
56
+ This is also the schema `scripts/parse-scan-results.mjs` normalizes toward (it keys on `id` + `package`
57
+ for dedup and sorts by `severity`).