start-vibing-stacks 2.14.0 → 2.16.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.
@@ -1,62 +1,139 @@
1
1
  ---
2
2
  name: research-cache
3
- version: 1.0.0
3
+ version: 2.0.0
4
+ description: "Caches web research findings under `.claude/skills/research-cache/cache/` to avoid redundant searches. Each entry has YAML frontmatter (`expires_on`, `sources`, `tier`, `engines`) so a session can grep-filter fresh entries without reading the body. Consumed by `research-web` v2.0.0 BEFORE any MCP / WebSearch call."
4
5
  ---
5
6
 
6
- # Research Cache — Best Practices Storage
7
+ # Research Cache — Best-Practices Storage (v2.0.0)
7
8
 
8
- **ALWAYS invoke BEFORE any web research.**
9
+ **ALWAYS invoke BEFORE any web research.** This skill is the **read AND write** side of `research-web` v2.0.0.
9
10
 
10
- ## Purpose
11
-
12
- Caches research findings to avoid redundant searches and maintain institutional knowledge.
13
-
14
- ## Structure
11
+ ## Layout
15
12
 
16
13
  ```
17
14
  .claude/skills/research-cache/
18
15
  ├── SKILL.md
19
16
  └── cache/
20
- ├── php-uuid-performance.md
21
- ├── laravel-octane-memory.md
22
- └── [topic].md
17
+ ├── _index.json # auto-derived, optional (one record per cache entry)
18
+ └── <topic-slug>.md # one file per topic. ≤ 8 KB. Pruned at expires_on.
19
+ ```
20
+
21
+ ## Cache entry frontmatter (token-efficient lookup)
22
+
23
+ Every cache entry MUST start with this frontmatter so a `grep` over `cache/*.md` filters fresh entries without parsing bodies:
24
+
25
+ ```yaml
26
+ ---
27
+ topic: <kebab-case>
28
+ stack: php | nodejs | python | universal | frontend
29
+ researched_on: YYYY-MM-DD
30
+ expires_on: YYYY-MM-DD # researched_on + 30 days (60 for stable specs, 14 for moving libraries)
31
+ tier: 1 | 2 # 1 = MCP web-scraper used; 2 = built-in WebSearch fallback
32
+ engines: [brave, vertex, grok] # which engines contributed (Tier 1 only)
33
+ sources_count: <int>
34
+ confidence: high | medium | low # high = 3+ official-doc sources agree; low = single blog post
35
+ ---
23
36
  ```
24
37
 
25
- ## Before Searching
38
+ ## Read protocol (lookup BEFORE searching)
26
39
 
27
- 1. Check cache: `ls .claude/skills/research-cache/cache/`
28
- 2. Grep for topic: `grep -rl "topic" .claude/skills/research-cache/cache/`
29
- 3. If found and recent (<30 days) → USE CACHE
30
- 4. If not found → Research and CREATE cache entry
40
+ ```bash
41
+ TOPIC="<kebab-case>"
42
+ TODAY=$(date +%F)
31
43
 
32
- ## Cache Entry Template
44
+ # 1. Direct hit?
45
+ F=".claude/skills/research-cache/cache/${TOPIC}.md"
46
+ if [ -f "$F" ]; then
47
+ EXPIRES=$(awk -F': ' '/^expires_on:/{print $2; exit}' "$F")
48
+ [ "$EXPIRES" \> "$TODAY" ] && echo "FRESH cache hit: $F" && exit 0
49
+ echo "STALE: $F (expired $EXPIRES); will refresh"
50
+ fi
51
+
52
+ # 2. Fuzzy hit? (alias / synonym)
53
+ grep -l "topic:.*${TOPIC%%-*}" .claude/skills/research-cache/cache/*.md 2>/dev/null
54
+ ```
55
+
56
+ If FRESH → use the cache, do NOT re-search.
57
+ If STALE or MISSING → research, then write a new entry (overwrite or create).
58
+
59
+ ## Write protocol (after research)
60
+
61
+ Always overwrite — never append. Stale data is worse than missing data.
62
+
63
+ ### Body template
33
64
 
34
65
  ```markdown
35
- # Research: {Topic}
66
+ ---
67
+ topic: <kebab-case>
68
+ stack: <one-of>
69
+ researched_on: 2026-05-13
70
+ expires_on: 2026-06-12
71
+ tier: 1
72
+ engines: [brave, vertex, grok]
73
+ sources_count: 5
74
+ confidence: high
75
+ ---
76
+
77
+ # Research: <Topic Title>
36
78
 
37
- **Date:** YYYY-MM-DD
38
- **Stack:** PHP|Node.js|Universal
39
- **Sources:** [list]
40
- **Expires:** YYYY-MM-DD (30 days from research)
79
+ > **TL;DR** (≤ 3 lines). The actionable answer in 30 seconds.
41
80
 
42
- ## Key Findings
43
- 1. Finding — Source: [URL]
81
+ ## Findings (cited)
82
+
83
+ | # | Finding | Source | Date | Tier |
84
+ |---|---|---|---|---|
85
+ | 1 | <one-line> | https://docs.example.com/foo | 2026-04-12 | official |
86
+ | 2 | <one-line> | https://eng-blog.example.com/bar | 2026-03-30 | engineering |
44
87
 
45
88
  ## Recommendations
46
- - Actionable recommendation
47
89
 
48
- ## Code Examples
49
- \`\`\`php
50
- // Example implementation
51
- \`\`\`
90
+ - <actionable rule, ready to drop into a skill or CLAUDE.md>
91
+ - <do this, not that>
92
+
93
+ ## Anti-patterns
94
+
95
+ - <something to avoid + why>
96
+
97
+ ## Code reference (optional)
98
+
99
+ - File / line / commit pointing at where this rule should be applied — NOT inline code dumps.
100
+
101
+ ## Open questions
52
102
 
53
- ## References
54
- - [URL] — [Description]
103
+ - <thing the research did NOT answer; flag for next round>
55
104
  ```
56
105
 
106
+ ## Expiry policy (cost vs freshness)
107
+
108
+ | Topic kind | TTL | Example |
109
+ |---|---|---|
110
+ | Specification (W3C, RFC, OpenAPI) | 60 days | WCAG 2.2 conformance |
111
+ | Stable framework (Laravel, Django, Express) | 30 days | Laravel 12 sessions |
112
+ | Fast-moving library (any minor < 1.0, anything pre-release) | 14 days | shadcn registries, AI SDKs |
113
+ | Security advisory | 7 days | CVE feeds, OWASP draft |
114
+
115
+ After `expires_on`, the entry is read-only history — `research-web` will refresh it on the next lookup.
116
+
117
+ ## Source ranking (recorded in each finding row)
118
+
119
+ 1. **official** — vendor docs, RFCs, W3C, MDN
120
+ 2. **engineering** — engineering blogs of the project (vercel.com/blog, fastify.io/blog)
121
+ 3. **community** — high-signal: Stack Overflow with 50+ votes, GitHub issues with maintainer response
122
+ 4. **opinion** — random blog post — accepted only if it confirms an official-doc claim, never as primary
123
+
57
124
  ## Rules
58
125
 
59
- 1. **CHECK CACHE FIRST** — avoid redundant searches
60
- 2. **CITE SOURCES** — every finding needs URL
61
- 3. **DATE ENTRIES** — for freshness checks
62
- 4. **STACK-SPECIFIC** — tag by stack for filtering
126
+ 1. **CHECK CACHE FIRST** — frontmatter `expires_on` lookup is one `awk`. Cheaper than any web call.
127
+ 2. **OVERWRITE, NEVER APPEND** — stale information is worse than missing.
128
+ 3. **CITE EVERY FINDING** — URL + date + tier. No bare claims.
129
+ 4. **TTL BY TOPIC KIND** — see table above. A spec lasts longer than a pre-release library.
130
+ 5. **CONFIDENCE EXPLICIT** — `high` requires ≥ 3 official-doc sources agreeing; `low` is single source.
131
+ 6. **NO INLINE CODE DUMPS** — link to file/line; the model can `Read` when it needs the bytes.
132
+ 7. **NO PII / SECRETS / CUSTOMER DATA** — never quote env values, tokens, internal hostnames.
133
+ 8. **PROMOTE WHAT'S UNIVERSAL** — if a rule applies project-wide, surface it for inclusion in `CLAUDE.md` (do not auto-edit).
134
+
135
+ ## See Also
136
+
137
+ - `research-web` v2.0.0 — MCP-first researcher; reads/writes this cache
138
+ - `claude-md-compactor` v2.0.0 — receives promoted rules; enforces 20 KB CLAUDE.md budget
139
+ - `mcp-web-scraper` skill — the MCP server providing Tier 1 search/scrape capability
@@ -1,12 +1,14 @@
1
1
  ---
2
2
  name: secrets-management
3
- version: 1.0.0
4
- description: Environment variable hygiene, secret detection, rotation, and secret-store patterns. Invoke whenever .env, secrets, API keys, or env-var-reading code are touched.
3
+ version: 2.0.0
4
+ description: "Environment variable hygiene, OIDC federation for CI/CD (2026 default — replaces long-lived static secrets), secret detection (gitleaks 3-layer: pre-commit + CI + GitHub push protection), rotation, and secret-store patterns. Invoke whenever .env, secrets, API keys, env-var-reading code, or CI cloud-deploy credentials are touched."
5
5
  ---
6
6
 
7
7
  # Secrets Management
8
8
 
9
- **ALWAYS invoke when touching `.env`, `process.env`, `os.environ`, secrets, API keys, or auth credentials.**
9
+ **ALWAYS invoke when touching `.env`, `process.env`, `os.environ`, secrets, API keys, auth credentials, or CI workflows that authenticate to cloud providers.**
10
+
11
+ > 2026 reality check: **28.65 million** new hardcoded secrets were added to public GitHub repositories in 2025 (+34% YoY, source: Snyk State of Secrets). The threat model is "an attacker is already scanning every commit you push, in real time."
10
12
 
11
13
  ## Core Rules
12
14
 
@@ -16,6 +18,7 @@ description: Environment variable hygiene, secret detection, rotation, and secre
16
18
  4. **No secrets in error messages** returned to clients.
17
19
  5. **`.env` is in `.gitignore`. `.env.example` is committed.** No exceptions.
18
20
  6. **Rotate after any leak.** Even suspected. The risk window is the time it takes to rotate, not when you think the leak started.
21
+ 7. **Prefer OIDC federation over static cloud creds in CI.** A leaked OIDC trust policy needs branch-+-repo match to be exploited; a leaked AWS access key works from anywhere until rotated.
19
22
 
20
23
  ---
21
24
 
@@ -122,35 +125,115 @@ return [
122
125
  | **Vercel/Netlify env vars** | Simple deployments |
123
126
  | **AWS Secrets Manager** / **GCP Secret Manager** / **Azure Key Vault** | Cloud-native, IAM-scoped |
124
127
  | **Doppler** | Multi-environment sync |
125
- | **HashiCorp Vault** | On-prem / self-hosted, dynamic creds |
126
- | **SOPS + age** | Git-encrypted secrets (GitOps) |
128
+ | **HashiCorp Vault** | On-prem / self-hosted, dynamic creds (auto-revoked) |
129
+ | **SOPS + age** | Git-encrypted secrets (GitOps) — `age` recommended over PGP for new repos (no keyring management, single-line keys) |
127
130
  | **1Password Connect / op-cli** | Team-shared dev secrets |
128
131
 
129
132
  **Rule:** `.env.production` is **never** committed. Production secrets live in the platform store and are injected at deploy/runtime.
130
133
 
134
+ ### 2026 reference architecture (defense in depth)
135
+
136
+ 1. **Short-lived credentials via OIDC** for CI/CD → no static cloud secrets
137
+ 2. **Encrypted secrets in Git via SOPS+age** for app config that must travel with code
138
+ 3. **Dynamic secrets via Vault** for DB/API tokens generated on-demand and auto-revoked
139
+
131
140
  ---
132
141
 
133
- ## DetectionBlock Before Commit
142
+ ## OIDC Federation CI/CD Without Static Cloud Secrets *(2026 default)*
143
+
144
+ GitHub Actions presents a JWT to the cloud's STS; cloud returns a short-lived (≤ 1h) credential. **No long-lived `AWS_SECRET_ACCESS_KEY` in repo secrets.**
145
+
146
+ ### AWS — IAM Role + Trust Policy
147
+
148
+ ```json
149
+ {
150
+ "Version": "2012-10-17",
151
+ "Statement": [{
152
+ "Effect": "Allow",
153
+ "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com" },
154
+ "Action": "sts:AssumeRoleWithWebIdentity",
155
+ "Condition": {
156
+ "StringEquals": {
157
+ "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
158
+ },
159
+ "StringLike": {
160
+ "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
161
+ }
162
+ }
163
+ }]
164
+ }
165
+ ```
134
166
 
135
- ### gitleaks (recommended)
167
+ ```yaml
168
+ permissions:
169
+ id-token: write # MANDATORY for OIDC
170
+ contents: read
171
+ jobs:
172
+ deploy:
173
+ runs-on: ubuntu-latest
174
+ steps:
175
+ - uses: aws-actions/configure-aws-credentials@v4
176
+ with:
177
+ role-to-assume: arn:aws:iam::ACCOUNT_ID:role/gha-deployer
178
+ aws-region: us-east-1
179
+ - run: aws s3 sync ./dist s3://my-bucket
180
+ ```
181
+
182
+ ### GCP — Workload Identity Federation
183
+ ```yaml
184
+ - uses: google-github-actions/auth@v2
185
+ with:
186
+ workload_identity_provider: 'projects/.../providers/github'
187
+ service_account: 'gha-deployer@my-project.iam.gserviceaccount.com'
188
+ ```
189
+
190
+ ### npm publish — provenance via OIDC (no `NPM_TOKEN`)
191
+ ```yaml
192
+ permissions:
193
+ id-token: write
194
+ contents: read
195
+ - run: npm publish --provenance --access public
196
+ ```
197
+
198
+ **Anti-pattern:** storing `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` as repo secrets. If you can use OIDC, you must.
199
+
200
+ ---
201
+
202
+ ## Detection — Three-Layer Defense *(2026 standard)*
203
+
204
+ A single layer is insufficient. Deploy **all three** so a bypass at one layer is caught at the next.
205
+
206
+ ### Layer 1 — Local pre-commit hook (developer machine)
207
+
208
+ Catches before the secret ever leaves the laptop.
136
209
 
137
- Install:
138
210
  ```bash
139
211
  brew install gitleaks # macOS
140
212
  # or: docker run --rm -v $(pwd):/repo zricethezav/gitleaks:latest detect -s /repo
141
213
  ```
142
214
 
143
- Pre-commit hook:
215
+ `.git/hooks/pre-commit` (or via `pre-commit` framework):
144
216
  ```bash
145
217
  gitleaks protect --staged --redact --verbose
146
218
  ```
147
219
 
148
- CI step:
220
+ Gitleaks ships rules for **150+ credential patterns** (AWS, GCP, GitHub, Stripe, OpenAI, Anthropic, Slack, JWT, private keys, etc.).
221
+
222
+ ### Layer 2 — CI scan on every PR (server-side)
223
+
224
+ Blocks PRs that slipped past Layer 1.
225
+
149
226
  ```yaml
150
227
  - name: Gitleaks scan
151
228
  uses: gitleaks/gitleaks-action@v2
229
+ env:
230
+ GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # only needed for org accounts
152
231
  ```
153
232
 
233
+ ### Layer 3 — GitHub Push Protection (platform-side)
234
+
235
+ Enable in **Settings → Code security → Secret scanning → Push protection**. GitHub's own scanner blocks the `git push` itself for known provider patterns. Free for public repos; **GitHub Advanced Security** for private.
236
+
154
237
  ### Quick grep (as a fallback)
155
238
 
156
239
  ```bash
@@ -233,13 +316,16 @@ Lockfile rules:
233
316
 
234
317
  - [ ] `.env` in `.gitignore`
235
318
  - [ ] `.env.example` updated when new var added
236
- - [ ] gitleaks (or fallback grep) clean
319
+ - [ ] gitleaks (Layer 1) clean
237
320
  - [ ] No secret in code, tests, fixtures, comments, logs
238
321
  - [ ] No `NEXT_PUBLIC_*SECRET|*TOKEN|*PRIVATE` patterns
239
322
  - [ ] Env vars validated at boot (Zod / Pydantic / config())
323
+ - [ ] CI workflows that touch cloud use **OIDC**, not static keys
324
+ - [ ] GitHub Push Protection enabled on the repo
240
325
 
241
326
  ## See Also
242
327
 
243
- - `security-baseline` — broader OWASP scope
328
+ - `security-baseline` — broader OWASP scope (2025-A03 Software Supply Chain Failures)
244
329
  - `observability` — log redaction details
330
+ - `ci-pipelines` — OIDC patterns + workflow permissions
245
331
  - Stack `api-security-*` — usage patterns
@@ -1,15 +1,38 @@
1
1
  ---
2
2
  name: security-baseline
3
- version: 1.0.0
4
- description: Universal OWASP Top 10 baseline with stack-aware examples. Invoke before designing or reviewing any feature that touches user data, auth, persistence, or external IO.
3
+ version: 2.0.0
4
+ description: "Universal OWASP Top 10 baseline with stack-aware examples. Covers BOTH OWASP 2021 (numbering kept stable for security-auditor §A01–§A10 cross-references) AND OWASP 2025 deltas (Software Supply Chain Failures new at A03, Mishandling Exceptional Conditions new at A10, SSRF demoted into Broken Access Control). Invoke before designing or reviewing any feature that touches user data, auth, persistence, supply chain, or external IO."
5
5
  ---
6
6
 
7
- # Security Baseline — OWASP Top 10 (2021)
7
+ # Security Baseline — OWASP Top 10 (2021 + 2025 deltas)
8
8
 
9
- **ALWAYS invoke when designing auth, APIs, persistence, file uploads, or any user-input flow.**
9
+ **ALWAYS invoke when designing auth, APIs, persistence, file uploads, supply-chain integrations, or any user-input flow.**
10
10
 
11
11
  > Threat model first. Then code. Defense in depth: every layer assumes the previous one failed.
12
12
 
13
+ ## OWASP Top 10 — 2021 vs 2025 (released Nov 2025)
14
+
15
+ OWASP Top 10:2025 was published at OWASP Global AppSec DC in November 2025. Material changes:
16
+
17
+ | 2021 | 2025 | Status |
18
+ |---|---|---|
19
+ | A01 Broken Access Control | A01 Broken Access Control | Same #1; **SSRF folded in here** |
20
+ | A02 Cryptographic Failures | A04 Cryptographic Failures | Demoted |
21
+ | A03 Injection | A05 Injection | Demoted |
22
+ | A04 Insecure Design | A06 Insecure Design | Demoted |
23
+ | A05 Security Misconfiguration | **A02 Security Misconfiguration** | Promoted (most observed) |
24
+ | A06 Vulnerable Components | (covered by 2025-A03) | Subsumed |
25
+ | A07 Authentication Failures | A07 Authentication Failures | Same |
26
+ | A08 Software & Data Integrity | A08 Software or Data Integrity Failures | Same |
27
+ | A09 Security Logging Failures | A09 Security Logging and Alerting Failures | Same |
28
+ | A10 SSRF | (folded into A01) | **Removed as a top-10 category — STILL DANGEROUS, see A01** |
29
+ | — | **A03 Software Supply Chain Failures** | **NEW** (was A06 Vulnerable Components, expanded) |
30
+ | — | **A10 Mishandling of Exceptional Conditions** | **NEW** (error handling that fails open / leaks state) |
31
+
32
+ **This skill keeps the §A01–§A10 anchors using the 2021 numbering** because cross-references in `security-auditor` v2.0.0, `api-security-*` skills, and stack overlays already cite them. The 2025 deltas are documented inline (see §A03+, §A06, §A10 sections).
33
+
34
+ ---
35
+
13
36
  ## Core Principles
14
37
 
15
38
  1. **Trust no input** — validate at every boundary (HTTP, queue, file, env, IPC).
@@ -114,11 +137,41 @@ User.findOne({ email });
114
137
 
115
138
  ---
116
139
 
117
- ## A06 — Vulnerable Components
140
+ ## A06 — Vulnerable Components *(2025: subsumed into the new A03 — Software Supply Chain Failures)*
118
141
 
119
142
  - Run `npm audit` / `pip-audit` / `composer audit` in CI.
120
143
  - Pin lockfiles. Use Dependabot/Renovate.
121
- - See `supply-chain` notes in `secrets-management` skill.
144
+ - See `supply-chain` notes in `secrets-management` skill and the **A03+ (2025) Software Supply Chain Failures** section below.
145
+
146
+ ---
147
+
148
+ ## A03+ (2025 only) — Software Supply Chain Failures *(NEW in 2025)*
149
+
150
+ Expanded from 2021's A06. Treats the **build, dependency, and distribution pipeline** as part of the threat surface.
151
+
152
+ | Threat | Mitigation |
153
+ |---|---|
154
+ | Malicious dependency (typosquat, dependency confusion) | Lock to private registries first; pin by hash where possible (npm `--integrity`, pip hashes) |
155
+ | Compromised maintainer / token | Require 2FA on maintainer accounts; mandatory provenance for first-party publishes |
156
+ | Build pipeline injection | Pin GitHub Actions by SHA, not tag; least-privilege `permissions:`; OIDC for cloud auth |
157
+ | Unsigned artifacts | Sign releases (Sigstore / cosign); verify signatures at deploy |
158
+ | `postinstall` script abuse | `npm config set ignore-scripts true` for CI installs; allowlist trusted packages |
159
+ | Lockfile drift | Fail CI when lockfile changes without manual review |
160
+
161
+ ```yaml
162
+ # Pin GitHub Actions by SHA
163
+ - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
164
+ - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
165
+
166
+ # Provenance + ignore scripts
167
+ permissions:
168
+ id-token: write # OIDC for npm publish provenance
169
+ contents: read
170
+ - run: npm ci --ignore-scripts
171
+ - run: npm publish --provenance --access public
172
+ ```
173
+
174
+ See `secrets-management` (OIDC federation), `ci-pipelines` (pinning + provenance), and `secrets-management` (28M secrets leaked on GitHub in 2025 — supply chain hygiene matters).
122
175
 
123
176
  ---
124
177
 
@@ -153,11 +206,14 @@ const event = stripe.webhooks.constructEvent(rawBody, sig, secret);
153
206
 
154
207
  ---
155
208
 
156
- ## A10 — Server-Side Request Forgery (SSRF)
209
+ ## A10 — Server-Side Request Forgery (SSRF) *(2025: folded into A01 — Broken Access Control)*
210
+
211
+ OWASP 2025 removed SSRF as a standalone category and treats it as a sub-class of A01. **The risk did not decrease** — apply the same controls. The numbering here is preserved so cross-references in `security-auditor` and per-stack overlays keep working.
157
212
 
158
213
  - Allowlist outbound destinations when fetching user-supplied URLs.
159
214
  - Block private IP ranges (10/8, 172.16/12, 192.168/16, 169.254/16, ::1, fc00::/7).
160
215
  - Resolve DNS yourself and check the IP — defeats DNS rebinding.
216
+ - Cloud metadata endpoints (`169.254.169.254`, `metadata.google.internal`) — **always block**, regardless of allowlist.
161
217
 
162
218
  ```ts
163
219
  import { isIP } from 'net';
@@ -169,6 +225,41 @@ if (PRIVATE.test(ip.address)) throw new ForbiddenError('Private IP not allowed')
169
225
 
170
226
  ---
171
227
 
228
+ ## A10+ (2025 only) — Mishandling of Exceptional Conditions *(NEW in 2025)*
229
+
230
+ Errors that **fail open**, leak internals, or leave the system in an inconsistent state. Distinct from A09 (which is about *whether* you logged it) — this is about *how the code reacts*.
231
+
232
+ | Anti-pattern | Why dangerous | Fix |
233
+ |---|---|---|
234
+ | `try { ... } catch (e) { /* ignore */ }` | Swallows security-relevant failures (e.g. authz check threw) | Log + rethrow, OR explicit comment why it's safe to ignore |
235
+ | Auth check inside `try`, success path outside | If auth throws, code may continue as if authenticated | Authz **fails closed**: throw → caller treats as denied |
236
+ | 500 with full stack trace to client | Leaks file paths, library versions, ORM internals | Generic message to client; full trace to logger only |
237
+ | Webhook handler returns 500 on parse error | Provider retries forever; floods queue | Validate signature first; on parse error log + return 200 (idempotently dropped) |
238
+ | Partial write on transaction failure | Money charged but order not created | Wrap multi-step writes in a transaction; on error, rollback all |
239
+ | Default-allow on missing config (`if (!config.requireMfa) skip`) | Misconfiguration → security off | Default-deny: missing config = block |
240
+
241
+ ```ts
242
+ // WRONG — fail open
243
+ try {
244
+ await assertCanAccess(user, resource);
245
+ } catch (e) {
246
+ console.log(e);
247
+ // ...code continues, request succeeds
248
+ }
249
+
250
+ // CORRECT — fail closed
251
+ try {
252
+ await assertCanAccess(user, resource);
253
+ } catch (e) {
254
+ logger.warn({ event: 'authz.error', err: e, userId: user.id, resourceId: resource.id });
255
+ throw e; // caller maps to 403
256
+ }
257
+ ```
258
+
259
+ See `error-handling` skill for full taxonomy and Result-type patterns.
260
+
261
+ ---
262
+
172
263
  ## Mandatory Boundary Validation
173
264
 
174
265
  Every external input MUST be validated by a schema:
@@ -185,18 +276,24 @@ No exceptions for "trusted" sources. Internal services drift, queues replay malf
185
276
 
186
277
  ## Pre-Commit Security Checklist
187
278
 
188
- - [ ] All routes have authn + authz checks
189
- - [ ] User ID derived from session, never from body
190
- - [ ] All inputs validated by schema
191
- - [ ] No secrets in code (see `secrets-management`)
192
- - [ ] No raw SQL / Mongo operators from user input
193
- - [ ] Security headers set on responses
194
- - [ ] Rate limit on auth + write endpoints
195
- - [ ] PII not in logs (see `observability`)
196
- - [ ] Webhook signatures verified before parsing
279
+ - [ ] All routes have authn + authz checks (§A01)
280
+ - [ ] User ID derived from session, never from body (§A01)
281
+ - [ ] All inputs validated by schema (§A03)
282
+ - [ ] No secrets in code (see `secrets-management`) (§A02 + 2025-A03)
283
+ - [ ] No raw SQL / Mongo operators from user input (§A03)
284
+ - [ ] Security headers set on responses (§A05)
285
+ - [ ] Rate limit on auth + write endpoints (§A07)
286
+ - [ ] PII not in logs (see `observability`) (§A09)
287
+ - [ ] Webhook signatures verified before parsing (§A08)
288
+ - [ ] CI pins GitHub Actions by SHA + uses OIDC for cloud auth (2025-A03)
289
+ - [ ] No silent `catch` blocks; authz failures rethrow (2025-A10)
290
+ - [ ] Cloud metadata endpoints blocked in any URL-fetching code (§A10)
197
291
 
198
292
  ## See Also
199
293
 
200
- - `secrets-management` — env hygiene, gitleaks
201
- - `observability` — log redaction
294
+ - `secrets-management` — env hygiene, gitleaks, OIDC federation
295
+ - `observability` — log redaction, PII handling
296
+ - `error-handling` — fail-closed patterns (2025-A10)
297
+ - `ci-pipelines` — pinning + provenance (2025-A03)
202
298
  - `api-security-node` / `api-security-python` / PHP `api-security` — stack-specific hardening
299
+ - Stack overlays consume the §A01–§A10 anchors above. **Do not renumber.**