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,94 @@
1
+ # Mocking, Fixtures, and Determinism
2
+
3
+ Tests must be **isolated** (independent of each other) and **deterministic** (same result
4
+ every run). That means controlling everything that crosses a boundary or varies between runs:
5
+ time, randomness, network, filesystem, and shared collaborators.
6
+
7
+ ## Test doubles — know which one you need
8
+
9
+ | Double | Purpose |
10
+ |---|---|
11
+ | **Dummy** | Filler passed to satisfy a signature; never used. |
12
+ | **Stub** | Returns canned answers to calls made during the test. |
13
+ | **Spy** | A stub that also records how it was called. |
14
+ | **Mock** | A double with pre-set expectations that verifies interactions. |
15
+ | **Fake** | A working but lightweight implementation (in-memory DB, fake clock). |
16
+
17
+ Reach for the **simplest** double that does the job. Prefer **fakes and stubs** over strict
18
+ mocks: asserting on exact call sequences couples the test to implementation and makes
19
+ refactoring break green tests.
20
+
21
+ ## Mock at the boundary, only the boundary
22
+
23
+ ```ts
24
+ // GOOD: mock the HTTP client (a true external boundary)
25
+ const httpGet = vi.fn().mockResolvedValue({ status: 200, body: { rate: 1.1 } });
26
+ const rate = await fetchExchangeRate(httpGet, 'USD', 'EUR');
27
+ expect(rate).toBe(1.1);
28
+ ```
29
+
30
+ ```ts
31
+ // BAD: mocking the function under test leaves nothing real to verify
32
+ const fetchExchangeRate = vi.fn().mockResolvedValue(1.1); // tests the mock, not the code
33
+ ```
34
+
35
+ Over-mocking is a top cause of tests that pass while the system is broken. If a test is
36
+ almost entirely mock setup, you are testing the mock framework. Either widen the unit or move
37
+ the check to an integration test with a real collaborator.
38
+
39
+ ## Controlling time
40
+
41
+ Wall-clock time is the most common flakiness source. Inject a clock or freeze it:
42
+
43
+ ```ts
44
+ import { vi } from 'vitest';
45
+
46
+ vi.useFakeTimers();
47
+ vi.setSystemTime(new Date('2026-06-29T12:00:00Z'));
48
+
49
+ expect(isExpired(token)).toBe(false);
50
+
51
+ vi.advanceTimersByTime(60_000); // jump 60s deterministically
52
+ expect(isExpired(token)).toBe(true);
53
+
54
+ vi.useRealTimers(); // always restore
55
+ ```
56
+
57
+ Never assert on `Date.now()` or sleep with real timeouts — that makes tests slow and racy.
58
+
59
+ ## Controlling randomness
60
+
61
+ Seed the RNG or inject it. A test that depends on `Math.random()` passes-then-fails at random:
62
+
63
+ ```ts
64
+ const rng = () => 0.42; // injected, deterministic
65
+ expect(pickWinner(players, rng)).toBe('alice');
66
+ ```
67
+
68
+ ## Fixtures and factories
69
+
70
+ - **Factories over literals.** A `makeOrder(overrides)` helper keeps tests readable and
71
+ resilient — when a required field is added, you change the factory once, not 50 tests.
72
+ - **Build only what the test needs.** Override just the fields relevant to the behavior;
73
+ let the factory default the rest. This makes the *intent* of each test obvious.
74
+ - **Isolate state.** Reset shared resources (DB rows, in-memory stores, module mocks) in
75
+ `beforeEach`/`afterEach` so tests can run in any order. Order-dependent tests are a bug.
76
+
77
+ ```ts
78
+ function makeOrder(overrides = {}) {
79
+ return { id: 'ord_1', status: 'open', total: 100, createdAt: new Date(0), ...overrides };
80
+ }
81
+
82
+ it('marks a paid order as fulfilled', () => {
83
+ const order = makeOrder({ status: 'paid' }); // only the relevant field is set
84
+ expect(fulfill(order).status).toBe('fulfilled');
85
+ });
86
+ ```
87
+
88
+ ## Determinism checklist
89
+
90
+ - [ ] No real network, filesystem, or DB in unit tests (use fakes/stubs).
91
+ - [ ] Time and randomness are frozen or injected.
92
+ - [ ] No shared mutable state leaks between tests; setup/teardown resets it.
93
+ - [ ] All async work is awaited — no dangling promises or unhandled timers.
94
+ - [ ] The suite passes when run in a randomized order.
@@ -0,0 +1,60 @@
1
+ # Test Types and the Test Pyramid
2
+
3
+ Not all tests are equal. Choosing the right *level* for a given behavior is the difference
4
+ between a suite that gives fast, reliable signal and one that is slow, flaky, and ignored.
5
+
6
+ ## The pyramid
7
+
8
+ ```
9
+ /\ E2E / UI few, slow, high-confidence, brittle
10
+ / \
11
+ /----\ Integration some, medium speed, real collaborators
12
+ / \
13
+ /--------\ Unit many, fast, isolated, cheap
14
+ ```
15
+
16
+ Push the bulk of your assertions **down** to the unit level where they are fast and stable.
17
+ Use the higher levels sparingly to prove the pieces wire together, not to re-test logic that
18
+ unit tests already cover. An inverted pyramid (mostly E2E) is slow and flaky.
19
+
20
+ ## Unit tests
21
+
22
+ - **Scope:** one function, method, or class in isolation. Collaborators that cross a process
23
+ or I/O boundary are replaced with doubles.
24
+ - **Cost:** milliseconds. Run thousands on every save.
25
+ - **Owns:** business logic, pure functions, edge cases, error branches, algorithms.
26
+ - **Rule of thumb:** if a behavior can be tested at the unit level, test it there.
27
+
28
+ ## Integration tests
29
+
30
+ - **Scope:** several real units working together, or one unit against a real adjacent
31
+ dependency (a database, an in-memory queue, a local HTTP server).
32
+ - **Cost:** tens to hundreds of milliseconds. Often need setup/teardown.
33
+ - **Owns:** the seams — ORM queries against a real schema, repository wiring, serialization,
34
+ framework request/response handling, transaction behavior.
35
+ - **Avoid:** re-testing branch logic here that a unit test already covers; keep these about
36
+ the *wiring*, not the *rules*.
37
+
38
+ ## End-to-end (E2E) tests
39
+
40
+ - **Scope:** the whole system through its real entry point — a browser, a CLI, a public API.
41
+ - **Cost:** seconds. Slow, infrastructure-heavy, the most prone to flakiness.
42
+ - **Owns:** a handful of critical user journeys (sign up, checkout, the money path).
43
+ - **Hand off:** browser-driven E2E belongs to the **e2e-playwright** skill, not this agent.
44
+
45
+ ## What goes where — a decision guide
46
+
47
+ | Question | Level |
48
+ |---|---|
49
+ | Does `applyDiscount(49.99, 10)` round correctly? | Unit |
50
+ | Does an invalid percentage throw `RangeError`? | Unit |
51
+ | Does the repository persist and re-read an order from Postgres? | Integration |
52
+ | Does `POST /orders` return `201` with a `Location` header? | Integration |
53
+ | Can a user add to cart and complete checkout in the browser? | E2E |
54
+
55
+ ## Coverage as a signal, not a goal
56
+
57
+ The 80% target is a floor that flags untested regions, not a trophy. 100% line coverage with
58
+ assertion-free tests proves nothing. Chase **branch and behavior** coverage — every error
59
+ path and boundary — over raw line percentage. Use `scripts/check-coverage.mjs` to gate the
60
+ floor in CI, and read the uncovered lines to find the cases you forgot.
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ // Coverage gate: reads an Istanbul/c8/Jest-style coverage-summary.json and fails (exit 1)
3
+ // if total coverage falls below a threshold. Zero dependencies — Node built-ins only.
4
+ //
5
+ // A coverage-summary.json looks like:
6
+ // { "total": { "lines": { "pct": 92.3 }, "statements": { "pct": 90.1 },
7
+ // "functions": { "pct": 85.0 }, "branches": { "pct": 88.2 } }, ... }
8
+ //
9
+ // Usage:
10
+ // node check-coverage.mjs coverage/coverage-summary.json # gate at 80% (default)
11
+ // node check-coverage.mjs coverage/coverage-summary.json --min=90 # custom threshold
12
+ // node check-coverage.mjs --min=75 < coverage-summary.json # read JSON from stdin
13
+ // node check-coverage.mjs --selftest # built-in test cases
14
+
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const DEFAULT_MIN = 80;
18
+ // Metrics we gate on; the lowest of these must meet the threshold.
19
+ const METRICS = ['lines', 'statements', 'functions', 'branches'];
20
+
21
+ // Pull the per-metric pct numbers out of a coverage summary's `total` block.
22
+ export function extractTotals(summary) {
23
+ if (!summary || typeof summary !== 'object' || !summary.total) {
24
+ throw new Error('missing "total" block in coverage summary');
25
+ }
26
+ const totals = {};
27
+ for (const m of METRICS) {
28
+ const pct = summary.total?.[m]?.pct;
29
+ if (typeof pct === 'number' && Number.isFinite(pct)) totals[m] = pct;
30
+ }
31
+ if (Object.keys(totals).length === 0) {
32
+ throw new Error('no numeric coverage metrics found in "total"');
33
+ }
34
+ return totals;
35
+ }
36
+
37
+ // Returns { ok, min, worst, totals, failures } — pure, so it is unit-testable.
38
+ export function evaluateCoverage(summary, min = DEFAULT_MIN) {
39
+ const totals = extractTotals(summary);
40
+ const failures = Object.entries(totals)
41
+ .filter(([, pct]) => pct < min)
42
+ .map(([metric, pct]) => ({ metric, pct }));
43
+ const worst = Math.min(...Object.values(totals));
44
+ return { ok: failures.length === 0, min, worst, totals, failures };
45
+ }
46
+
47
+ function parseMin(argv) {
48
+ const flag = argv.find((a) => a.startsWith('--min='));
49
+ if (!flag) return DEFAULT_MIN;
50
+ const n = Number(flag.slice('--min='.length));
51
+ if (!Number.isFinite(n) || n < 0 || n > 100) {
52
+ console.error(`✗ invalid --min value: "${flag}" (expected 0–100)`);
53
+ process.exit(1);
54
+ }
55
+ return n;
56
+ }
57
+
58
+ function report(label, result) {
59
+ const pad = (m) => m.padEnd(11);
60
+ for (const [metric, pct] of Object.entries(result.totals)) {
61
+ const mark = pct < result.min ? '✗' : '✓';
62
+ console.log(` ${mark} ${pad(metric)} ${pct.toFixed(2)}%`);
63
+ }
64
+ if (result.ok) {
65
+ console.log(`✓ ${label}: coverage ${result.worst.toFixed(2)}% meets ${result.min}% threshold`);
66
+ } else {
67
+ const names = result.failures.map((f) => `${f.metric} ${f.pct.toFixed(2)}%`).join(', ');
68
+ console.error(`✗ ${label}: below ${result.min}% threshold — ${names}`);
69
+ }
70
+ return result.ok;
71
+ }
72
+
73
+ function runFile(path, min) {
74
+ let summary;
75
+ try {
76
+ summary = JSON.parse(readFileSync(path, 'utf8'));
77
+ } catch (err) {
78
+ console.error(`✗ could not read/parse "${path}": ${err.message}`);
79
+ process.exit(1);
80
+ }
81
+ try {
82
+ process.exit(report(path, evaluateCoverage(summary, min)) ? 0 : 1);
83
+ } catch (err) {
84
+ console.error(`✗ ${path}: ${err.message}`);
85
+ process.exit(1);
86
+ }
87
+ }
88
+
89
+ function runStdin(min) {
90
+ let buf = '';
91
+ process.stdin.on('data', (c) => (buf += c));
92
+ process.stdin.on('end', () => {
93
+ try {
94
+ process.exit(report('stdin', evaluateCoverage(JSON.parse(buf), min)) ? 0 : 1);
95
+ } catch (err) {
96
+ console.error(`✗ stdin: ${err.message}`);
97
+ process.exit(1);
98
+ }
99
+ });
100
+ }
101
+
102
+ function selftest() {
103
+ const passing = { total: {
104
+ lines: { pct: 92.3 }, statements: { pct: 90.1 },
105
+ functions: { pct: 85.0 }, branches: { pct: 88.2 },
106
+ } };
107
+ const failing = { total: {
108
+ lines: { pct: 72.0 }, statements: { pct: 70.5 },
109
+ functions: { pct: 60.0 }, branches: { pct: 55.0 },
110
+ } };
111
+ const edge = { total: { lines: { pct: 80.0 }, statements: { pct: 80.0 },
112
+ functions: { pct: 80.0 }, branches: { pct: 80.0 } } }; // exactly at threshold -> pass
113
+
114
+ const cases = [
115
+ ['passing above 80', evaluateCoverage(passing, 80).ok, true],
116
+ ['failing below 80', evaluateCoverage(failing, 80).ok, false],
117
+ ['exactly at 80 passes', evaluateCoverage(edge, 80).ok, true],
118
+ ['passing fails a 95 gate', evaluateCoverage(passing, 95).ok, false],
119
+ ['worst metric is the branch pct', evaluateCoverage(passing, 80).worst === 85.0, true],
120
+ ];
121
+
122
+ let allOk = true;
123
+ for (const [name, actual, expected] of cases) {
124
+ const correct = actual === expected;
125
+ allOk &&= correct;
126
+ console.log(`${correct ? '✓' : '✗'} selftest ${name}: got ${actual} (expected ${expected})`);
127
+ }
128
+ // Error handling: malformed input must throw.
129
+ let threw = false;
130
+ try { extractTotals({ nope: true }); } catch { threw = true; }
131
+ console.log(`${threw ? '✓' : '✗'} selftest rejects malformed summary: threw ${threw} (expected true)`);
132
+ allOk &&= threw;
133
+
134
+ process.exit(allOk ? 0 : 1);
135
+ }
136
+
137
+ const argv = process.argv.slice(2);
138
+ if (argv.includes('--selftest')) {
139
+ selftest();
140
+ } else {
141
+ const min = parseMin(argv);
142
+ const file = argv.find((a) => !a.startsWith('--'));
143
+ if (file) runFile(file, min);
144
+ else runStdin(min);
145
+ }
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: threat-modeler
3
+ description: Use when designing a new system or feature, or assessing the attack surface of an existing one. Produces a STRIDE threat model over a data-flow diagram — assets, trust boundaries, threats, risk ratings, and prioritized mitigations.
4
+ tools: Read, Grep, Glob, Write
5
+ model: claude-opus-4-8
6
+ type: agent
7
+ version: 2.0.0
8
+ updated: 2026-06-29
9
+ ---
10
+ # Threat Modeler
11
+
12
+ You are a threat-modeling specialist. You think like an attacker *during design*, when mitigations are
13
+ cheapest to apply, and you answer two questions for every system you look at: **what can go wrong**, and
14
+ **what are we doing about it?** You produce a structured, defensible threat model — not a vague list of
15
+ "security concerns" — that an engineering team can act on directly.
16
+
17
+ You operate read-only: you analyze designs, code, and architecture documents to build the model. You do
18
+ not implement fixes; you specify them and hand them to the people who do.
19
+
20
+ ## Role and operating principles
21
+
22
+ - **Boundaries first.** The most valuable threats live where data crosses from a less-trusted zone to a
23
+ more-trusted one. Find those crossings before enumerating anything.
24
+ - **Asset-driven, not technology-driven.** Start from what is worth protecting (data, funds, credentials,
25
+ availability, reputation) and who wants it. A clever attack on something worthless is noise.
26
+ - **Every threat gets a decision.** Each threat is rated and assigned an outcome: *mitigate*, *accept*,
27
+ *transfer*, or *avoid*. An unrated threat is an unfinished one.
28
+ - **Defense in depth.** Prefer layered controls over a single point of failure. Assume any one control
29
+ can be bypassed and ask what catches the attacker next.
30
+ - **Concrete over generic.** "Validate input" is not a mitigation; "reject `order_id` values that fail a
31
+ ULID format check before the DB query, server-side" is. Tie every mitigation to a specific threat.
32
+ - **Honest uncertainty.** Flag assumptions you could not verify and name the single riskiest one to
33
+ validate first. Do not invent endpoints, auth flows, or data stores that aren't in the material.
34
+
35
+ ## Workflow: STRIDE over a data-flow diagram
36
+
37
+ Work through these steps in order. Do not skip to threats before the diagram exists — ungrounded threat
38
+ lists are the most common failure of this exercise.
39
+
40
+ 1. **Scope and assets.** State what is in and out of scope. List the assets worth protecting and rank
41
+ them. Name the adversaries (external attacker, malicious user, compromised dependency, insider) and
42
+ their capabilities.
43
+ 2. **Build the data-flow diagram (DFD).** Identify the four element types — *external entities*,
44
+ *processes*, *data stores*, and *data flows* — and draw how data moves between them. Render it as
45
+ text (see below) so it lives in version control alongside the design.
46
+ 3. **Mark trust boundaries.** Draw a boundary wherever data crosses a privilege, network, or ownership
47
+ line (browser → API, API → DB, your service → third-party). Every flow that crosses a boundary is a
48
+ prime target.
49
+ 4. **Enumerate threats with STRIDE.** For each element, walk the six categories. Not every category
50
+ applies to every element — use the element-type mapping in `references/stride-method.md`.
51
+
52
+ | STRIDE category | Violates | Typical threat |
53
+ |---|---|---|
54
+ | **S**poofing | Authentication | attacker impersonates a user or service |
55
+ | **T**ampering | Integrity | request/data/store is modified in transit or at rest |
56
+ | **R**epudiation | Non-repudiation | actor denies an action; no audit trail |
57
+ | **I**nformation disclosure | Confidentiality | data leaks to an unauthorized party |
58
+ | **D**enial of service | Availability | resource is exhausted or made unavailable |
59
+ | **E**levation of privilege | Authorization | actor gains rights they shouldn't have |
60
+
61
+ 5. **Rate each threat.** Score *likelihood × impact* (see Risk rating below) to get a severity, then
62
+ assign an outcome and rationale.
63
+ 6. **Specify mitigations.** Map each *mitigate* decision to a concrete, testable control. Identify the
64
+ riskiest assumption to validate first and the top three threats to fix before launch.
65
+
66
+ ## Risk rating
67
+
68
+ Use a simple, repeatable scale so ratings are comparable across the model. Score Likelihood and Impact
69
+ each Low(1)/Medium(2)/High(3); severity is the product, bucketed:
70
+
71
+ - **Critical (6–9):** plausible attack with severe consequence — block launch until mitigated.
72
+ - **High (3–4):** likely or high-impact — fix before release where feasible.
73
+ - **Medium (2):** fix soon; track explicitly.
74
+ - **Low (1):** accept or monitor; document the rationale.
75
+
76
+ Anchor "impact" to the asset ranking from step 1, not to how interesting the exploit is. A defaced
77
+ marketing page and a leaked credentials table are not the same severity even if both are "tampering."
78
+
79
+ ## Output format
80
+
81
+ Produce the model in this order. Keep it tight — depth belongs in the linked references, not inline.
82
+
83
+ 1. **Scope & assets** — in/out of scope, ranked assets, adversaries.
84
+ 2. **Data-flow diagram** — text DFD with numbered elements and marked trust boundaries.
85
+ 3. **STRIDE threat table** — one row per threat: ID, element, category, description, likelihood, impact,
86
+ severity, decision.
87
+ 4. **Mitigations** — one row per *mitigate* threat: threat ID → concrete control → how to verify it.
88
+ 5. **Accepted/transferred risks** — explicit, each with rationale and owner.
89
+ 6. **Top risks & riskiest assumption** — the 3 to fix first and the 1 to validate first.
90
+
91
+ ### Threat table shape
92
+
93
+ ```text
94
+ | ID | Element | STRIDE | Threat | L | I | Sev | Decision |
95
+ |-----|--------------------|--------|-----------------------------------------|---|---|-----|-----------|
96
+ | T1 | (4) API → DB flow | T | SQL injection via order_id param | 3 | 3 | 9 | Mitigate |
97
+ | T2 | (2) Auth process | S | Credential stuffing on /login | 3 | 2 | 6 | Mitigate |
98
+ | T3 | (5) Audit log | R | User denies refund; no signed audit row | 2 | 2 | 4 | Mitigate |
99
+ | T4 | (3) Session store | I | Session token readable in Redis at rest | 2 | 3 | 6 | Mitigate |
100
+ | T5 | (1) Public CDN | D | Cache-busting flood exhausts origin | 2 | 2 | 4 | Accept |
101
+ ```
102
+
103
+ ### DFD as text
104
+
105
+ ```text
106
+ TRUST BOUNDARY: Internet ││ Private network
107
+
108
+ (E1) Browser ──1: HTTPS POST /login──►││──► (P2) Auth Service ──3: query──►││──► (DS3) User DB
109
+ │ ││ │ ││
110
+ └──────2: GET /orders (JWT)──────►││──► (P4) Order API ──4: SQL──────►││──► (DS5) Orders DB
111
+ ││ │
112
+ ││ └──5: write──► (DS6) Audit Log
113
+ ││ │
114
+ ││ └──6: POST──►││──► (E7) Stripe API
115
+ Legend: (E)=external entity (P)=process (DS)=data store N: data flow ││ = trust boundary
116
+ ```
117
+
118
+ ## Common pitfalls and failure modes
119
+
120
+ - **Threats with no diagram.** Listing "XSS, CSRF, SQLi" without a DFD produces a checklist, not a model
121
+ scoped to *this* system. Always ground threats in a specific element.
122
+ - **Boiling the ocean.** Enumerating every STRIDE category for every internal, fully-trusted element
123
+ buries the real risks. Concentrate on boundary-crossing flows and high-value assets.
124
+ - **Unrated threats.** A threat without a likelihood, impact, and decision is just anxiety. Rate it or
125
+ cut it.
126
+ - **Mitigations that don't map.** A mitigation list that doesn't reference threat IDs can't be checked
127
+ for completeness. Every *mitigate* threat needs a control; every control needs a threat.
128
+ - **Silent accepted risks.** Accepting a risk is fine — accepting it *without writing it down and naming
129
+ an owner* is how it becomes an incident no one expected.
130
+ - **Confusing authn with authz.** Spoofing (who are you) and Elevation of privilege (what may you do) are
131
+ different categories with different controls. Don't collapse them.
132
+ - **Treating the model as one-and-done.** A threat model is a living document; it must be revisited when
133
+ the architecture, data, or trust boundaries change.
134
+
135
+ ## When NOT to use / boundaries
136
+
137
+ - **Not a penetration test.** This is a design-time analysis from documents and code, not active
138
+ exploitation of a running system. For live testing, hand off to a pentest engagement.
139
+ - **Not a code-level vulnerability scan.** For line-by-line findings in changed code (injection sinks,
140
+ unsafe crypto calls, secrets), use the `security-auditor` agent — this agent operates at the
141
+ architecture level.
142
+ - **Not a compliance audit.** Mapping to SOC 2 / ISO 27001 controls is a different exercise; a threat
143
+ model can *feed* one but isn't a substitute.
144
+ - **Skip for trivial changes** with no new trust boundary, new data, or new external surface — the
145
+ overhead won't pay off.
146
+ - **Don't fabricate architecture.** If the design material is too thin to identify boundaries, say so and
147
+ ask for the missing flows rather than guessing.
148
+
149
+ ## Files in this package
150
+
151
+ - `references/stride-method.md` — the STRIDE categories in depth, the element-type → category mapping,
152
+ and a per-element question checklist.
153
+ - `references/dfd-and-trust-boundaries.md` — how to build a data-flow diagram and where to draw trust
154
+ boundaries, with the text notation used above.
155
+ - `references/mitigation-catalog.md` — concrete, reusable mitigations indexed by STRIDE category.
156
+ - `examples/threat-model.md` — a complete worked threat model for a small checkout service.
157
+ - `examples/dfd-example.md` — a standalone annotated DFD with its trust boundaries explained.
158
+ - `scripts/stride-checklist.mjs` — runnable Node check that a threat-model doc covers all six STRIDE
159
+ categories for each element; supports `--selftest`.
160
+
161
+ Pairs with the `security-auditor` agent (for code-level vulnerability findings), the `architect` agent
162
+ (for the system design the model is built on), and the [`owasp-top10`](../../skills/owasp-top10/) skill
163
+ (for mapping threats to the most common web weakness classes).
164
+
165
+
166
+ ## Memory — learn across sessions
167
+
168
+ You keep a persistent, per-project memory at `.claude/memory/threat-modeler.md`. It is
169
+ how you get sharper on *this* codebase over time instead of starting cold every run.
170
+
171
+ - **Before you start:** read `.claude/memory/threat-modeler.md` if it exists and apply what
172
+ it holds — corrections you were given before, this project's conventions, decisions
173
+ and their rationale, and recurring pitfalls. If it is missing, continue without it.
174
+ - **After you finish:** if this task taught you something durable — a correction from
175
+ the user, a project-specific convention, a mistake worth not repeating — append it as
176
+ a short dated bullet under a relevant heading, and prune anything now stale or wrong.
177
+ Keep entries terse and general.
178
+ - **Never record** secrets, credentials, tokens, personal data, or one-off trivia, and
179
+ never write anywhere except your own `.claude/memory/` file.
@@ -0,0 +1,55 @@
1
+ # DFD Example — Annotated
2
+
3
+ A standalone data-flow diagram with its trust boundaries explained, to illustrate the notation before you
4
+ build your own. The system: a file-upload feature where users upload documents that a worker processes.
5
+
6
+ ## The diagram
7
+
8
+ ```text
9
+ TRUST BOUNDARY A: Internet ││ App tier TRUST BOUNDARY B: App tier ┊┊ Worker tier
10
+
11
+ (E1) User ──1: HTTPS PUT /files (JWT)──►││──► (P2) Upload API ──2: store object──►││──► (DS3) Object Store
12
+ ││ │ ││
13
+ ││ └──3: enqueue job────────────►││──► (DS4) Job Queue
14
+ ││ ┊┊
15
+ ││ (P5) Worker ◄──4: dequeue──┊┊── (DS4) Job Queue
16
+ ││ │
17
+ ││ └──5: read object──► (DS3) Object Store
18
+ ││ │
19
+ ││ └──6: write result──► (DS6) Results DB
20
+
21
+ Legend: (E)=external entity (P)=process (DS)=data store
22
+ N: numbered data flow ││ = network/privilege boundary ┊┊ = process/tier boundary
23
+ ```
24
+
25
+ ## Why the boundaries are where they are
26
+
27
+ - **Boundary A (Internet → App tier):** flow 1 carries untrusted, attacker-controllable input — the file
28
+ bytes, the filename, the content-type header, and the JWT. Everything arriving here must be
29
+ authenticated (Spoofing), authorized (Elevation of privilege), and validated (Tampering). This is the
30
+ single most important boundary in the system.
31
+ - **Boundary B (App tier → Worker tier):** the job queue is a *trust boundary even though it's internal*.
32
+ The worker (P5) consumes whatever the Upload API enqueued plus the object it stored. If an attacker can
33
+ influence the filename or object contents, the worker is processing attacker-controlled data — so
34
+ parsing in P5 (think: image/PDF/zip parsers) is a prime Tampering and Elevation-of-privilege target,
35
+ even though no public flow touches the worker directly.
36
+
37
+ ## Threats that fall out of this diagram
38
+
39
+ Reading along the crossing flows:
40
+
41
+ - **Flow 1 → P2:** unrestricted file upload (a `.php`/`.svg` masquerading as an image) → Elevation of
42
+ privilege / stored XSS. Path traversal in the filename → Tampering of the object store.
43
+ - **DS3 Object Store:** public-readable bucket → Information disclosure of other users' files.
44
+ Missing ownership check on later retrieval → IDOR (Elevation of privilege).
45
+ - **Flow 4 → P5:** a malicious file enqueued earlier is parsed by the worker → memory-corruption or
46
+ decompression-bomb DoS in the parser. The worker often runs with more privileges than the API — a
47
+ parser exploit here is high impact.
48
+ - **DS6 Results DB:** if results include rendered user content, stored XSS can resurface when displayed.
49
+
50
+ ## Takeaways
51
+
52
+ 1. Internal queues and worker tiers are still behind trust boundaries — model them.
53
+ 2. The most dangerous element is often not the public API but the *background process* that parses what
54
+ the public API accepted.
55
+ 3. Number flows so the threat table can reference them unambiguously (e.g. "T-flow1-E", "T-flow4-T").
@@ -0,0 +1,81 @@
1
+ # Threat Model — "QuickCheckout" Checkout Service
2
+
3
+ A complete, worked threat model for a small checkout service. Use it as a template for shape and depth.
4
+
5
+ ## 1. Scope & assets
6
+
7
+ **In scope:** the public checkout API, its auth, the orders and session stores, and the Stripe
8
+ integration. **Out of scope:** the marketing site, internal analytics pipeline.
9
+
10
+ **Assets (ranked):**
11
+ 1. Customers' stored payment method tokens and order/PII data — highest value.
12
+ 2. Account credentials / session tokens.
13
+ 3. Integrity of order and refund records (financial correctness).
14
+ 4. Service availability during a sale.
15
+
16
+ **Adversaries:** unauthenticated external attacker; authenticated malicious customer (abuse/IDOR);
17
+ compromised third-party JS dependency; opportunistic bot networks.
18
+
19
+ ## 2. Data-flow diagram
20
+
21
+ ```text
22
+ TRUST BOUNDARY: Internet ││ Private network
23
+
24
+ (E1) Browser ──1: POST /login──────────►││──► (P2) Auth Service ──3: read/write──►││──► (DS3) User DB
25
+ │ ││ │ ││
26
+ │ ││ └──4: read/write session──────►││──► (DS4) Session Cache
27
+ │ ││
28
+ └──2: POST /checkout (JWT)─────────►││──► (P5) Order API ──6: SQL────────────►││──► (DS6) Orders DB
29
+ ││ │
30
+ ││ ├──7: append──────────────────►││──► (DS7) Audit Log
31
+ ││ │
32
+ ││ └──8: POST /charge─────────────►││──► (E8) Stripe API
33
+ Legend: (E)=external entity (P)=process (DS)=data store N: flow ││ = trust boundary
34
+ ```
35
+
36
+ ## 3. STRIDE threat table
37
+
38
+ ```text
39
+ | ID | Element | STRIDE | Threat | L | I | Sev | Decision |
40
+ |-----|----------------------|--------|-------------------------------------------------|---|---|-----|-----------|
41
+ | T1 | (2) /checkout flow | T | SQL injection via cart item id into Orders DB | 3 | 3 | 9 | Mitigate |
42
+ | T2 | (P2) Auth Service | S | Credential stuffing against /login | 3 | 2 | 6 | Mitigate |
43
+ | T3 | (2) /checkout (JWT) | E | IDOR: user A submits order referencing user B | 3 | 3 | 9 | Mitigate |
44
+ | T4 | (DS4) Session Cache | I | Session tokens readable in Redis at rest | 2 | 3 | 6 | Mitigate |
45
+ | T5 | (DS7) Audit Log | R | User disputes refund; log row is editable | 2 | 2 | 4 | Mitigate |
46
+ | T6 | (8) Stripe flow | I | Card data passes through our server unnecessarily| 2 | 3 | 6 | Avoid |
47
+ | T7 | (P5) Order API | D | Checkout flood exhausts DB connection pool | 2 | 2 | 4 | Mitigate |
48
+ | T8 | (E1) Browser | T | Compromised third-party JS skims card form | 2 | 3 | 6 | Mitigate |
49
+ | T9 | (P2) Auth Service | I | Verbose error reveals which emails are registered| 2 | 1 | 2 | Mitigate |
50
+ ```
51
+
52
+ ## 4. Mitigations
53
+
54
+ ```text
55
+ | Threat | Control | Verify by |
56
+ |--------|----------------------------------------------------------------------|--------------------------------------------|
57
+ | T1 | Parameterized queries only; reject non-ULID item ids at boundary | Static check + injection test in CI |
58
+ | T2 | Per-account + per-IP rate limit, lockout, MFA option | Load test login; assert 429 after N tries |
59
+ | T3 | Server-side ownership check: order.user_id == token.sub on every read | Authz test: user A cannot fetch B's order |
60
+ | T4 | Encrypt session cache at rest; short TTL; rotate on privilege change | Confirm KMS-backed encryption; TTL config |
61
+ | T5 | Append-only audit store; logs outside app write path; signed rows | Attempt UPDATE on audit row → denied |
62
+ | T7 | Bounded conn pool + per-key rate limit + queue with back-pressure | Stress test; assert graceful 429, no crash |
63
+ | T8 | Use Stripe-hosted fields/iframe; SRI on third-party scripts; CSP | CSP report-only in staging; SRI hashes set |
64
+ | T9 | Generic "invalid credentials" response; constant-time compare | Test login with unknown vs known email |
65
+ ```
66
+
67
+ ## 5. Accepted / transferred risks
68
+
69
+ - **T6 (avoided):** card data will never transit our servers — we use Stripe Elements so the browser
70
+ posts card details directly to Stripe. This *avoids* the threat (and most PCI scope) rather than
71
+ mitigating it. Owner: Payments lead.
72
+ - **Volumetric DDoS at the network layer:** transferred to the CDN/WAF provider; accepted residual risk
73
+ of brief degradation during an extreme flood. Owner: SRE on-call.
74
+
75
+ ## 6. Top risks & riskiest assumption
76
+
77
+ **Fix before launch:** T1 (SQLi, sev 9), T3 (IDOR, sev 9), then T2 (credential stuffing, sev 6).
78
+
79
+ **Riskiest assumption to validate first:** that the JWT `sub` claim is validated server-side on *every*
80
+ order read and write. If any handler trusts a client-supplied `user_id` instead, T3 is exploitable
81
+ regardless of the other controls — verify this first.