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,134 @@
1
+ ---
2
+ name: secrets-management
3
+ description: Handle secrets safely across the lifecycle — keep them out of source, load from env or a secret manager, scope to least privilege, encrypt in transit and at rest, rotate on a schedule, and respond fast when one leaks. Deep reference with runbooks, examples, and a runnable leak scanner.
4
+ type: skill
5
+ version: 2.0.0
6
+ updated: 2026-06-29
7
+ ---
8
+ # Secrets Management
9
+
10
+ A secret committed to source control is **already compromised** — assume it is public the moment it lands
11
+ in history, even on a private repo. Git history, CI logs, container layers, backups, and forks all retain
12
+ it. The only safe response is rotation, not deletion. This skill is the deep reference for keeping secrets
13
+ out of code, supplying them safely at runtime, and reacting correctly when one escapes. Heavy detail lives
14
+ in `references/`; copy-paste material in `examples/`; a runnable leak scanner in `scripts/`.
15
+
16
+ ## Mental model
17
+
18
+ Treat a secret as **runtime configuration**, never as code. Code is committed, reviewed, copied, and
19
+ shipped to laptops; secrets must not ride along. Separate three concerns and never collapse them:
20
+
21
+ | Concern | Question | Answer |
22
+ |---|---|---|
23
+ | Storage | where does the truth live? | a secret manager (Vault, cloud KMS/Secrets Manager) |
24
+ | Delivery | how does the app get it? | injected env var or a fetch at boot, over TLS |
25
+ | Lifecycle | how does it change? | rotation on a schedule + on suspected leak |
26
+
27
+ The application code should only ever *read* a secret from its environment — it should never know the
28
+ storage backend, the rotation cadence, or the raw value's origin. That decoupling is what lets you rotate
29
+ a leaked key with **zero code changes**.
30
+
31
+ ## 1. Keep secrets out of source
32
+
33
+ 1. Never hardcode keys, tokens, passwords, connection strings, or private keys in source — not even
34
+ "temporarily". Temporary hardcodes are how most leaks happen.
35
+ 2. Commit a placeholder template (`.env.example`) with **fake** values and key names only; add the real
36
+ `.env` to `.gitignore` before the first commit.
37
+ 3. Add a secret scanner to pre-commit hooks **and** CI so a leak is caught before it reaches the remote.
38
+ Run `scripts/detect-hardcoded.mjs` over diffs or files as a zero-dependency gate.
39
+ 4. Load and validate required secrets at startup; fail fast with a clear message naming the missing var.
40
+
41
+ ```js
42
+ // Read from the environment; never embed the value. Validate at boot.
43
+ const required = ['DATABASE_URL', 'STRIPE_SECRET_KEY', 'JWT_SIGNING_KEY'];
44
+ const missing = required.filter((k) => !process.env[k]);
45
+ if (missing.length) {
46
+ throw new Error(`Missing required secrets: ${missing.join(', ')}`);
47
+ }
48
+ const stripeKey = process.env.STRIPE_SECRET_KEY; // if this leaks, rotate the value — no code change
49
+ ```
50
+
51
+ ## 2. Environment variables vs secret managers
52
+
53
+ Env vars are the universal **delivery** mechanism, but they are not a secure **store** — they leak via
54
+ `/proc`, crash dumps, child processes, and accidental `console.log(process.env)`. For anything beyond a
55
+ single dev machine, the source of truth belongs in a manager.
56
+
57
+ | Approach | Good for | Watch out for |
58
+ |---|---|---|
59
+ | `.env` file (gitignored) | local dev only | easy to commit by accident; no rotation, no audit |
60
+ | Platform env vars (CI/PaaS) | small apps, single-tenant | visible to anyone with dashboard access; static |
61
+ | Secret manager (Vault, AWS/GCP/Azure) | production, teams | needs auth bootstrap; adds a runtime dependency |
62
+ | Cloud KMS (envelope encryption) | encrypting data + secrets | key policy mistakes are silent until exploited |
63
+
64
+ A manager buys you **dynamic, short-lived credentials** (e.g. Vault issues a DB credential that auto-expires
65
+ in 1 hour), centralized **audit logs**, and **rotation without redeploys**. See
66
+ `references/secret-managers.md` for the comparison and bootstrap-auth patterns.
67
+
68
+ ## 3. Least privilege & short-lived credentials
69
+
70
+ - Scope every credential to the **minimum** resources and actions it needs; a build token does not need
71
+ prod database write access.
72
+ - Prefer **short-lived** dynamic credentials over long-lived static keys. A credential that lives 15
73
+ minutes limits the blast radius of a leak to 15 minutes.
74
+ - Use **distinct** credentials per service and per environment so revocation is surgical, not a blackout.
75
+ - Never share one "god" key across services — you lose the ability to attribute and revoke.
76
+
77
+ ## 4. Encryption in transit and at rest
78
+
79
+ - **In transit:** fetch secrets only over TLS; reject plaintext transports. Never pass secrets as URL
80
+ query params (they land in logs, proxies, and browser history) — use headers or the request body.
81
+ - **At rest:** the manager encrypts its store; for data you encrypt yourself, use **envelope encryption**
82
+ (a KMS-held key encrypts per-record data keys) rather than a single static key in config.
83
+ - Keep secrets out of logs, error reports, and APM traces. Redact at the logging boundary — see the
84
+ scanner's redaction logic and `references/leak-response.md`.
85
+
86
+ ## 5. Rotation
87
+
88
+ Rotation is the difference between "we had an incident" and "we had a non-event." Two triggers:
89
+
90
+ 1. **Scheduled** — rotate on a fixed cadence (e.g. 90 days for static keys) so no credential is ancient.
91
+ 2. **Reactive** — rotate **immediately** on any suspected exposure, no matter how minor it looks.
92
+
93
+ The safe pattern is **overlap**: provision the new credential, deploy it, verify, then revoke the old —
94
+ so there is no downtime window. The full step-by-step is in
95
+ [`references/rotation.md`](references/rotation.md) and a fill-in incident checklist is in
96
+ `examples/rotation-runbook.md`.
97
+
98
+ ## Common pitfalls (failure modes)
99
+
100
+ - **"It's a private repo, so it's fine."** Forks, clones, CI caches, and future open-sourcing all expose
101
+ it. Private is not secret.
102
+ - **Deleting the commit instead of rotating.** `git rm` / force-push does not erase history from clones,
103
+ caches, and PR mirrors. The credential is burned — rotate it.
104
+ - **Logging the whole environment.** `console.log(process.env)` or dumping config on a crash ships every
105
+ secret to your log aggregator. Redact at the boundary.
106
+ - **Secrets in URLs.** Query-string tokens leak into access logs, referrer headers, and proxies.
107
+ - **One static key forever.** No rotation means a years-old leak is still valid today.
108
+ - **Over-scoped credentials.** A read-only reporting job holding admin keys turns a small leak into a
109
+ full compromise.
110
+ - **Checking `.env.example` with real values.** The template must contain only obviously fake placeholders.
111
+
112
+ ## When NOT to use / trade-offs
113
+
114
+ - **A full secret manager for a solo side project** is overkill — gitignored `.env` plus a pre-commit
115
+ scanner is proportionate. Adopt a manager when you have a team, multiple environments, or compliance needs.
116
+ - **Short-lived dynamic credentials add operational complexity** (auth bootstrap, renewal, clock skew). If
117
+ your platform can't renew reliably, a rotated static key with tight scope may be the pragmatic choice.
118
+ - **Client-side secrets don't exist.** Anything shipped to a browser or mobile app is public; never put a
119
+ secret in front-end code — use a backend proxy or short-lived scoped tokens instead.
120
+ - **Encrypting everything** has key-management cost. Encrypt what's sensitive; don't build a KMS maze for
121
+ non-secret config.
122
+
123
+ ## Files in this package
124
+
125
+ - `references/secret-managers.md` — env vars vs Vault/cloud managers, bootstrap auth, dynamic creds
126
+ - `references/rotation.md` — scheduled & reactive rotation, zero-downtime overlap pattern
127
+ - `references/leak-response.md` — what to do in the first hour after a leak; redaction guidance
128
+ - `examples/env-example.md` — a model `.env.example` with safe placeholders and `.gitignore` notes
129
+ - `examples/rotation-runbook.md` — fill-in incident/rotation checklist
130
+ - `scripts/detect-hardcoded.mjs` — zero-dep scanner that flags & redacts hardcoded secrets (`--selftest`)
131
+
132
+ Pairs with the `secure-auth` skill (where signing keys and session secrets come from), the `owasp-top10`
133
+ skill (A07 identification/auth failures, A02 cryptographic failures), and the `security-auditor` agent for
134
+ a full credential-handling review.
@@ -0,0 +1,41 @@
1
+ # Model `.env.example`
2
+
3
+ Commit this template; never commit the real `.env`. Values here are **obviously fake placeholders** so a
4
+ scanner won't flag them and a human won't mistake them for real. Document every key the app needs.
5
+
6
+ ```dotenv
7
+ # === Database ===
8
+ # Real value lives in the secret manager; this is the shape only.
9
+ DATABASE_URL=postgres://USER:PASSWORD@localhost:5432/appdb
10
+
11
+ # === Third-party APIs ===
12
+ STRIPE_SECRET_KEY=sk_test_REPLACE_ME_xxxxxxxxxxxxxxxx
13
+ SENDGRID_API_KEY=SG.REPLACE_ME.xxxxxxxxxxxxxxxxxxxxxxxx
14
+
15
+ # === Auth / signing ===
16
+ # Generate locally with: openssl rand -base64 32
17
+ JWT_SIGNING_KEY=REPLACE_WITH_RANDOM_32_BYTES
18
+ SESSION_SECRET=REPLACE_WITH_RANDOM_32_BYTES
19
+
20
+ # === Runtime config (not secret) ===
21
+ NODE_ENV=development
22
+ LOG_LEVEL=info
23
+ ```
24
+
25
+ ## `.gitignore` (add before the first commit)
26
+
27
+ ```gitignore
28
+ # Real secrets — never commit
29
+ .env
30
+ .env.local
31
+ .env.*.local
32
+ *.pem
33
+ *.key
34
+ ```
35
+
36
+ ## Rules
37
+
38
+ 1. Keep `.env.example` in sync with the code — every `process.env.X` the app reads should appear here.
39
+ 2. Placeholders must be clearly fake: `REPLACE_ME`, `sk_test_...`, `xxxx`. Never a redacted-but-real value.
40
+ 3. The real `.env` is for local dev only. In CI/production, inject values from the secret manager.
41
+ 4. Validate presence at startup (see `SKILL.md` §1) so a missing key fails fast with a named error.
@@ -0,0 +1,50 @@
1
+ # Rotation / Leak Runbook (fill-in)
2
+
3
+ Copy this into your incident tracker and fill the blanks. The goal of the first hour is to make the leaked
4
+ credential useless — rotate first, investigate second.
5
+
6
+ ## Incident header
7
+
8
+ - **Date / time detected (UTC):** ________
9
+ - **Detected by:** (scanner / human / third-party report) ________
10
+ - **Credential type:** (API key / DB password / signing key / deploy token / other) ________
11
+ - **Where it leaked:** (commit SHA / log / screenshot / paste / laptop) ________
12
+ - **Likely exposure window start:** ________
13
+
14
+ ## Step 1 — Rotate / revoke (do this FIRST)
15
+
16
+ - [ ] Provision replacement credential
17
+ - [ ] Deploy / inject new value via secret manager
18
+ - [ ] Verify new credential works in production
19
+ - [ ] Revoke the leaked credential
20
+ - [ ] New credential id / version: ________
21
+
22
+ ## Step 2 — Assess blast radius
23
+
24
+ - [ ] What can this credential do? ________
25
+ - [ ] Which environments / data are reachable? ________
26
+ - [ ] Could it have unlocked other secrets? (list) ________
27
+
28
+ ## Step 3 — Check for abuse
29
+
30
+ - [ ] Pulled audit logs since exposure window: yes / no
31
+ - [ ] Unfamiliar IPs / regions / patterns found? ________
32
+ - [ ] Derived sessions/tokens revoked if needed: yes / n/a
33
+
34
+ ## Step 4 — Notify
35
+
36
+ - [ ] Owning team notified
37
+ - [ ] Security / compliance notified (if required by policy)
38
+
39
+ ## Step 5 — Cleanup (after rotation)
40
+
41
+ - [ ] Scrubbed value from git history (`git filter-repo`)
42
+ - [ ] Invalidated CI caches / mirrors
43
+ - [ ] Did NOT paste the raw secret into this ticket
44
+
45
+ ## Step 6 — Prevent recurrence
46
+
47
+ - [ ] Added/tightened scanner rule that missed it
48
+ - [ ] Pre-commit + CI secret gate in place
49
+ - [ ] Post-incident timeline written
50
+ - [ ] Owner: ________ Due: ________
@@ -0,0 +1,47 @@
1
+ # Leak Response — The First Hour
2
+
3
+ When a secret leaks, speed beats blame. The credential is burned; your job is to make it useless before it
4
+ is abused. Deleting the commit is **not** a fix — history persists in clones, forks, CI caches, and mirrors.
5
+
6
+ ## Immediate steps (in order)
7
+
8
+ 1. **Rotate / revoke the credential first.** Generate a replacement and invalidate the leaked value. This
9
+ is the only action that actually closes the hole. Everything else is cleanup.
10
+ 2. **Assess blast radius.** What could this credential do? Which environments, data, and actions? Use that
11
+ to set urgency and notification scope.
12
+ 3. **Check for abuse.** Pull audit logs for the credential since the likely exposure time. Look for
13
+ unfamiliar IPs, regions, or access patterns.
14
+ 4. **Contain.** If abuse is suspected, also revoke sessions/tokens derived from it and rotate any secrets
15
+ it could have unlocked (a leaked deploy key may have read other secrets).
16
+ 5. **Notify** the owning team and, if required, security/compliance — per your incident policy.
17
+ 6. **Purge history** only after rotation: scrub the value from git history (e.g. `git filter-repo`),
18
+ invalidate CI caches, and force-update. Treat this as hygiene, not remediation.
19
+ 7. **Post-incident:** add or tighten the scanner rule that missed it; add a pre-commit/CI gate if one was
20
+ absent; write up the timeline.
21
+
22
+ ## Redaction (prevent the next leak in logs)
23
+
24
+ Most leaks after the first are in logs. Redact at the logging boundary so secrets never reach the
25
+ aggregator:
26
+
27
+ ```js
28
+ const SECRET_KEYS = /(pass(word)?|secret|token|api[-_]?key|authorization)/i;
29
+ function redact(obj) {
30
+ if (typeof obj !== 'object' || obj === null) return obj;
31
+ const out = Array.isArray(obj) ? [] : {};
32
+ for (const [k, v] of Object.entries(obj)) {
33
+ out[k] = SECRET_KEYS.test(k) ? '[REDACTED]' : redact(v);
34
+ }
35
+ return out;
36
+ }
37
+ logger.info('request', redact(context)); // never log raw process.env or auth headers
38
+ ```
39
+
40
+ The package scanner (`scripts/detect-hardcoded.mjs`) redacts matched values in its output for the same
41
+ reason — never echo the secret you just found.
42
+
43
+ ## Don'ts
44
+
45
+ - Don't quietly delete the commit and move on — the credential is still valid until rotated.
46
+ - Don't paste the leaked value into a ticket, chat, or email to "show" the leak.
47
+ - Don't wait for confirmation of abuse before rotating; rotation is cheap, a breach is not.
@@ -0,0 +1,46 @@
1
+ # Secret Rotation
2
+
3
+ Rotation is the control that turns a leak into a non-event. A credential that is rotated regularly and can
4
+ be rotated *fast* limits how long any exposure is useful.
5
+
6
+ ## Two triggers
7
+
8
+ 1. **Scheduled rotation** — replace credentials on a fixed cadence so none becomes ancient.
9
+ - Long-lived static keys: every 30–90 days.
10
+ - Signing keys (JWT, sessions): rotate with overlap; keep the previous key valid for verification until
11
+ all issued tokens expire.
12
+ - Prefer **short-lived dynamic credentials** so "rotation" is automatic and continuous.
13
+ 2. **Reactive rotation** — rotate **immediately** on any suspected exposure: a key in a commit, a leaked
14
+ log, a laptop loss, a departing employee, or a third-party breach. Do not wait to "confirm" — rotate first.
15
+
16
+ ## Zero-downtime overlap pattern
17
+
18
+ Never rotate by deleting the old credential first — that causes an outage. Overlap instead:
19
+
20
+ 1. **Provision** a new credential alongside the old one (both valid).
21
+ 2. **Deploy / inject** the new value (update the secret manager; let instances pick it up).
22
+ 3. **Verify** the new credential works in production (health check, canary).
23
+ 4. **Revoke** the old credential.
24
+ 5. **Confirm** nothing broke; check error rates and audit logs for use of the revoked key.
25
+
26
+ ```
27
+ old key: valid ============================x (revoked at step 4)
28
+ new key: valid =========================================>
29
+ ^deploy ^verify ^safe to revoke old
30
+ ```
31
+
32
+ For **signing keys**, the overlap must outlast the longest token lifetime: publish both keys (by key id),
33
+ sign with the new one, accept either for verification, and drop the old key only after every token signed
34
+ with it has expired.
35
+
36
+ ## KMS / envelope-encryption key rotation
37
+
38
+ When you encrypt data yourself, rotate the **key-encryption key** (KEK) in KMS without re-encrypting every
39
+ record: data is encrypted with per-record data keys, which are themselves encrypted by the KEK. Rotating the
40
+ KEK re-wraps data keys lazily. Keep old KEK versions enabled for decryption until re-wrap completes.
41
+
42
+ ## Automate it
43
+
44
+ Manual rotation is rotation that doesn't happen. Use the manager's native rotation (AWS rotation Lambdas,
45
+ Vault leases, GCP versions) so the cadence is enforced by infrastructure, not a calendar reminder. See
46
+ `leak-response.md` for the reactive path and `examples/rotation-runbook.md` for a fill-in checklist.
@@ -0,0 +1,52 @@
1
+ # Secret Managers — Env Vars vs Vault vs Cloud KMS
2
+
3
+ Where the *truth* of a secret lives. Environment variables deliver secrets to a process; they do not
4
+ securely store them. For anything past a single laptop, the source of truth belongs in a manager.
5
+
6
+ ## Why env vars are not a store
7
+
8
+ Env vars are convenient and universal, but:
9
+ - They are inherited by **child processes** — a shelled-out command sees every secret.
10
+ - They appear in `/proc/<pid>/environ`, crash dumps, and many APM/observability agents.
11
+ - They are trivially leaked by `console.log(process.env)` or an error reporter that captures context.
12
+ - They are **static** — no rotation, no expiry, no audit trail of who read what.
13
+
14
+ Use them as the *delivery* channel (the app reads `process.env.X`), but populate them from a manager.
15
+
16
+ ## Options compared
17
+
18
+ | Manager | Model | Strengths | Costs / caveats |
19
+ |---|---|---|---|
20
+ | HashiCorp Vault | central server, dynamic secrets | short-lived DB/cloud creds, leasing, rich audit | run/operate a server; auth bootstrap |
21
+ | AWS Secrets Manager / SSM | managed, IAM-scoped | native rotation Lambdas, IAM policies | AWS-only; per-secret cost |
22
+ | GCP Secret Manager | managed, IAM-scoped | versioning, IAM, CMEK | GCP-only |
23
+ | Azure Key Vault | managed, RBAC + keys | HSM-backed keys, certs + secrets | Azure-only |
24
+ | Cloud KMS | key custody, not blob store | envelope encryption of your own data | you still store the ciphertext somewhere |
25
+
26
+ ## The bootstrap-auth problem
27
+
28
+ A manager removes secrets from your repo, but the app still needs **one** credential to authenticate to the
29
+ manager. Solve it with platform identity, not another static secret:
30
+ - **Workload identity / IAM roles** — the cloud assigns the running instance an identity; no key to store.
31
+ - **Vault auth methods** — Kubernetes service-account JWT, AWS IAM, or AppRole with a short-lived secret-id.
32
+ - **OIDC federation** in CI — the pipeline exchanges a signed OIDC token for short-lived cloud creds; no
33
+ long-lived keys in CI settings.
34
+
35
+ The goal: the only thing on disk is an *identity*, and the actual secrets are fetched at runtime over TLS.
36
+
37
+ ## Dynamic, short-lived credentials
38
+
39
+ The biggest win of a real manager is **generated-on-demand** credentials:
40
+ ```
41
+ # Conceptual: Vault issues a DB credential that auto-expires
42
+ GET vault/database/creds/app-readonly
43
+ -> { username: "v-app-ro-7f3a", password: "<generated>", lease_duration: 3600 }
44
+ ```
45
+ The credential lives one hour, is unique to this request, and is revoked automatically. A leak is bounded
46
+ by the lease, and every issuance is in the audit log.
47
+
48
+ ## Choosing
49
+
50
+ - Solo / hobby: gitignored `.env` + a pre-commit scanner. A manager is overkill.
51
+ - Team / multi-env / compliance: a managed secret manager with IAM-scoped access and rotation.
52
+ - Encrypting your own data at rest: KMS envelope encryption (see `rotation.md` for key rotation).
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ // Zero-dependency scanner: flags likely hardcoded secrets in a code string and
3
+ // redacts the matched value so the finding can be logged safely.
4
+ //
5
+ // Usage:
6
+ // node detect-hardcoded.mjs '<code string>' # scan a literal string
7
+ // echo '<code>' | node detect-hardcoded.mjs # scan from stdin
8
+ // node detect-hardcoded.mjs --selftest # built-in leaky + clean cases
9
+ //
10
+ // Exit codes: 0 = no secrets found (clean) ; 1 = secret(s) found OR selftest failed.
11
+
12
+ // Each rule: a name and a regex with the sensitive value in capture group 1 (or 0).
13
+ const RULES = [
14
+ ['AWS access key id', /\b(AKIA[0-9A-Z]{16})\b/],
15
+ ['Stripe secret key', /\b(sk_(?:live|test)_[A-Za-z0-9]{16,})\b/],
16
+ ['Google API key', /\b(AIza[0-9A-Za-z_\-]{35})\b/],
17
+ ['Slack token', /\b(xox[baprs]-[0-9A-Za-z-]{10,})\b/],
18
+ ['GitHub token', /\b(gh[pousr]_[0-9A-Za-z]{20,})\b/],
19
+ ['JWT', /\b(eyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,})\b/],
20
+ ['Private key block', /(-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----)/],
21
+ // Generic: an assignment to a secret-ish name with a non-trivial inline string literal.
22
+ ['Hardcoded password/secret assignment',
23
+ /\b(?:password|passwd|secret|api[_-]?key|token|access[_-]?key)\b\s*[:=]\s*["'`]([^"'`\s]{6,})["'`]/i],
24
+ ];
25
+
26
+ // Mask all but a short prefix so output never echoes a real secret.
27
+ function redactValue(v) {
28
+ if (v.length <= 6) return '*'.repeat(v.length);
29
+ return v.slice(0, 4) + '*'.repeat(Math.max(4, v.length - 4));
30
+ }
31
+
32
+ export function scan(code) {
33
+ const findings = [];
34
+ for (const [name, re] of RULES) {
35
+ const g = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g');
36
+ let m;
37
+ while ((m = g.exec(code)) !== null) {
38
+ const value = m[1] ?? m[0];
39
+ findings.push({ rule: name, redacted: redactValue(value) });
40
+ if (m.index === g.lastIndex) g.lastIndex++; // avoid zero-width loop
41
+ }
42
+ }
43
+ return findings;
44
+ }
45
+
46
+ function report(label, code) {
47
+ const findings = scan(code);
48
+ if (findings.length === 0) {
49
+ console.log(`OK ${label}: no hardcoded secrets detected`);
50
+ return true;
51
+ }
52
+ console.error(`FAIL ${label}: ${findings.length} potential secret(s):`);
53
+ for (const f of findings) console.error(` - ${f.rule}: ${f.redacted}`);
54
+ return false;
55
+ }
56
+
57
+ function selftest() {
58
+ // All sample values are OBVIOUSLY FAKE — fixed filler, not real credentials.
59
+ const leaky = [
60
+ "const k = 'AKIAIOSFODNN7EXAMPLE';",
61
+ "stripe = 'sk_test_0000000000000000FAKE';",
62
+ "googleKey = 'AIzaBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB';",
63
+ "const password = 'hunter2hunter2';",
64
+ "apiKey: 'tok_FAKEFAKEFAKE'",
65
+ ];
66
+ const clean = [
67
+ "const k = process.env.AWS_ACCESS_KEY_ID;",
68
+ "const password = process.env.DB_PASSWORD; // loaded from env",
69
+ "// example: set STRIPE_SECRET_KEY in your secret manager",
70
+ "const limit = 20; const name = 'orders';",
71
+ ];
72
+ let pass = true;
73
+ leaky.forEach((c, i) => {
74
+ const found = scan(c).length > 0;
75
+ const ok = found === true;
76
+ pass &&= ok;
77
+ console.log(`${ok ? 'OK' : 'XX'} leaky[${i}]: ${found ? 'flagged' : 'missed'} (expected flagged)`);
78
+ });
79
+ clean.forEach((c, i) => {
80
+ const found = scan(c).length > 0;
81
+ const ok = found === false;
82
+ pass &&= ok;
83
+ console.log(`${ok ? 'OK' : 'XX'} clean[${i}]: ${found ? 'flagged' : 'clean'} (expected clean)`);
84
+ });
85
+ console.log(pass ? 'selftest: PASS' : 'selftest: FAIL');
86
+ process.exit(pass ? 0 : 1);
87
+ }
88
+
89
+ const arg = process.argv[2];
90
+ if (arg === '--selftest') selftest();
91
+ else if (arg) process.exit(report('input', arg) ? 0 : 1);
92
+ else {
93
+ let buf = '';
94
+ process.stdin.on('data', (c) => (buf += c));
95
+ process.stdin.on('end', () => process.exit(report('stdin', buf) ? 0 : 1));
96
+ }
@@ -0,0 +1,135 @@
1
+ ---
2
+ name: secure-auth
3
+ description: Implement authentication securely — authentication vs authorization, password hashing (argon2id/bcrypt), sessions vs JWT (storage, expiry, refresh, revocation), MFA, OAuth2/OIDC flows, and defenses against credential stuffing, session fixation, and CSRF. Worked examples + a runnable password-policy check.
4
+ type: skill
5
+ version: 2.0.0
6
+ updated: 2026-06-29
7
+ ---
8
+ # Secure Authentication
9
+
10
+ Authentication is the front door to your system, and it is the single control attackers probe hardest.
11
+ The goal of this skill is **not** to teach you to invent a clever scheme — it is to help you assemble
12
+ well-understood primitives correctly, because almost every real-world breach in this area comes from a
13
+ broken assembly of good parts, not from cracked cryptography. Heavy detail lives in `references/`;
14
+ copy-paste material in `examples/`; a runnable policy check in `scripts/`.
15
+
16
+ ## Mental model
17
+
18
+ Three distinct questions get muddled constantly. Keep them separate:
19
+
20
+ | Question | Concern | Wrong answer looks like |
21
+ |---|---|---|
22
+ | Who are you? | **Authentication** (login, password, MFA) | trusting a client-supplied `user_id` |
23
+ | What may you do? | **Authorization** (roles, ownership, scopes) | checking auth but not ownership (IDOR) |
24
+ | How do we remember you? | **Session management** (cookies/tokens) | long-lived tokens you can't revoke |
25
+
26
+ A request can be perfectly *authenticated* and still be an attack if you skip *authorization*. The most
27
+ common API vulnerability — Broken Object Level Authorization — is exactly this: a logged-in user reads
28
+ `/accounts/124` when they only own `124`'s neighbor. Always check ownership server-side, never trust an
29
+ identifier the client could change.
30
+
31
+ ## 1. Password storage: slow hash, never plaintext
32
+
33
+ The non-negotiable rule: **never store a recoverable password.** Store a one-way hash produced by a
34
+ *deliberately slow* algorithm so that a stolen database is expensive to crack offline.
35
+
36
+ ```ts
37
+ import argon2 from 'argon2';
38
+
39
+ // Registration / password change — argon2id is the current default recommendation.
40
+ const hash = await argon2.hash(password, { type: argon2.argon2id });
41
+ // Store `hash` (it embeds the salt + cost params). NEVER store `password`.
42
+
43
+ // Login — constant-time verify; argon2 reads cost params from the stored hash.
44
+ const ok = await argon2.verify(hash, attempt);
45
+ ```
46
+
47
+ Use **argon2id** (preferred) or **bcrypt** (battle-tested, fine if argon2 isn't available). Never use
48
+ fast general-purpose hashes (`MD5`, `SHA-256`) — a GPU computes billions of those per second, so a leak
49
+ becomes a mass account takeover within hours. Always salt (argon2/bcrypt do this for you), and validate
50
+ the password against a policy and a breached-password list *before* hashing — run
51
+ `scripts/check-password-policy.mjs --selftest` to see the kind of check that belongs at this boundary.
52
+ The full parameter-tuning guidance is in [references/password-hashing.md](references/password-hashing.md).
53
+
54
+ ## 2. Sessions vs JWT: how you remember a logged-in user
55
+
56
+ Two dominant models, with a real trade-off around **revocation**:
57
+
58
+ - **Server-side sessions** — a random opaque session ID in an `HttpOnly; Secure; SameSite` cookie; state
59
+ lives server-side (DB/Redis). Revocation is trivial: delete the row. This is the safe default for
60
+ classic web apps.
61
+ - **Stateless JWTs** — a signed token the client carries. Scales without shared session storage, but you
62
+ **cannot un-issue a signed token** before it expires. Mitigate with short-lived access tokens
63
+ (5–15 min) plus a revocable, rotating refresh token kept server-side.
64
+
65
+ ```http
66
+ Set-Cookie: session=9f2c...; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
67
+ ```
68
+
69
+ Storage matters enormously: a token in `localStorage` is readable by any XSS payload, so prefer
70
+ `HttpOnly` cookies (which JavaScript cannot read) over web storage for anything that authenticates a
71
+ request. The complete comparison — expiry, refresh rotation, reuse detection, and revocation strategies —
72
+ is in [references/sessions-vs-jwt.md](references/sessions-vs-jwt.md).
73
+
74
+ ## 3. MFA and step-up
75
+
76
+ A password alone is a single point of failure against phishing and credential stuffing. Offer a second
77
+ factor and require it for sensitive actions (password change, payouts):
78
+
79
+ - **TOTP** (authenticator app) — good baseline, phishable but cheap.
80
+ - **WebAuthn / passkeys** — phishing-resistant, hardware-backed; prefer where you can.
81
+ - **SMS** — weakest (SIM-swap), use only as a fallback.
82
+
83
+ Treat MFA as *step-up*: don't force it on every request, escalate it when risk rises.
84
+
85
+ ## 4. OAuth2 / OIDC: delegated auth done right
86
+
87
+ When you offload login to a provider (Google, an identity platform), use **OAuth2 Authorization Code
88
+ flow with PKCE** for web and mobile/SPA clients. Do not use the implicit flow (deprecated) and never the
89
+ resource-owner password flow for third-party login. OIDC layers an identity `id_token` on top of OAuth2's
90
+ access token. Validate the `id_token` signature, `iss`, `aud`, and `exp`, and use the `state` parameter to
91
+ defend against CSRF on the callback. See [references/oauth2-oidc.md](references/oauth2-oidc.md) for the
92
+ full flow diagrams and validation checklist, and `examples/auth-flow.md` for an annotated walk-through.
93
+
94
+ ## 5. Account recovery
95
+
96
+ Recovery flows are a favorite bypass — they are authentication's back door. Make reset tokens
97
+ **single-use, time-limited (e.g. 15–30 min), and high-entropy**, store only their hash, and invalidate
98
+ all active sessions on a successful reset. Critically, return the **same response** whether or not the
99
+ email exists, and keep timing uniform, so the endpoint can't be used to enumerate accounts.
100
+
101
+ ## Common pitfalls (failure modes)
102
+
103
+ - **Fast/unsalted hashes** (`SHA-256`, `MD5`) — instantly crackable at scale after a DB leak.
104
+ - **JWT you can't revoke** — a long-lived access token stays valid after logout/compromise; keep them short.
105
+ - **Tokens in `localStorage`** — any XSS exfiltrates them; use `HttpOnly` cookies.
106
+ - **Session fixation** — not rotating the session ID on login lets an attacker pre-seed a known ID; always
107
+ regenerate the session identifier at the moment privilege changes.
108
+ - **Missing CSRF protection** on cookie-authenticated state-changing requests — use `SameSite` cookies
109
+ plus anti-CSRF tokens. See [references/auth-attacks.md](references/auth-attacks.md).
110
+ - **Account enumeration** — different responses/timing for "wrong password" vs "no such user."
111
+ - **No rate limiting / lockout** — credential stuffing walks straight in; throttle and add MFA.
112
+ - **Authn without authz** — checking *who* but not *whether they own this object* (IDOR/BOLA).
113
+
114
+ ## When NOT to roll your own / trade-offs
115
+
116
+ Prefer a vetted identity provider or library over hand-built auth whenever you can. Building it yourself
117
+ means owning password reset, MFA enrollment, session revocation, breach monitoring, and audit trails
118
+ *forever*. Reach for a managed IdP (OIDC provider, an auth platform) when you want SSO, social login, or
119
+ compliance offload; the cost is vendor lock-in and per-MAU pricing. Build in-house only when you have
120
+ hard data-residency constraints or genuinely unusual requirements — and even then, wrap proven libraries
121
+ (argon2, a maintained OAuth2 client) rather than primitives. Stateless JWT trades easy revocation for
122
+ horizontal scale; if you rarely need to scale auth across regions, server-side sessions are simpler and
123
+ safer.
124
+
125
+ ## Files in this package
126
+
127
+ - `references/password-hashing.md` — argon2id vs bcrypt, cost tuning, pepper, rehash-on-login
128
+ - `references/sessions-vs-jwt.md` — cookies vs tokens, expiry, refresh rotation, revocation
129
+ - `references/oauth2-oidc.md` — Authorization Code + PKCE, token validation, OIDC claims
130
+ - `references/auth-attacks.md` — credential stuffing, session fixation, CSRF, enumeration + defenses
131
+ - `examples/jwt-verify.ts` — dependency-light JWT verification with the right checks
132
+ - `examples/auth-flow.md` — annotated login + refresh + OAuth2 callback walk-through
133
+ - `scripts/check-password-policy.mjs` — runnable policy check (length, classes, breached list); `--selftest`
134
+
135
+ Pairs with `owasp-top10`, `secrets-management`, `rest-api-design`, `audit-logging`, and `gdpr-compliance`.