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.
- package/package.json +1 -1
- package/stacks/_shared/commands/feature.md +21 -9
- package/stacks/_shared/commands/fix.md +20 -6
- package/stacks/_shared/commands/research.md +18 -7
- package/stacks/_shared/commands/validate.md +31 -4
- package/stacks/_shared/skills/codebase-knowledge/SKILL.md +46 -49
- package/stacks/_shared/skills/codebase-knowledge/TEMPLATE.md +35 -14
- package/stacks/_shared/skills/docker-patterns/SKILL.md +184 -31
- package/stacks/_shared/skills/docs-tracker/SKILL.md +83 -35
- package/stacks/_shared/skills/git-workflow/SKILL.md +140 -17
- package/stacks/_shared/skills/hook-development/SKILL.md +230 -52
- package/stacks/_shared/skills/observability/SKILL.md +72 -2
- package/stacks/_shared/skills/openapi-design/SKILL.md +111 -3
- package/stacks/_shared/skills/playwright-automation/SKILL.md +173 -27
- package/stacks/_shared/skills/postgres-patterns/SKILL.md +85 -4
- package/stacks/_shared/skills/research-cache/SKILL.md +112 -35
- package/stacks/_shared/skills/secrets-management/SKILL.md +98 -12
- package/stacks/_shared/skills/security-baseline/SKILL.md +115 -18
- package/stacks/_shared/skills/ui-ux-audit/SKILL.md +94 -35
|
@@ -1,62 +1,139 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: research-cache
|
|
3
|
-
version:
|
|
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
|
|
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
|
-
##
|
|
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
|
-
├──
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
##
|
|
38
|
+
## Read protocol (lookup BEFORE searching)
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
4. If not found → Research and CREATE cache entry
|
|
40
|
+
```bash
|
|
41
|
+
TOPIC="<kebab-case>"
|
|
42
|
+
TODAY=$(date +%F)
|
|
31
43
|
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
##
|
|
43
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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** —
|
|
60
|
-
2. **
|
|
61
|
-
3. **
|
|
62
|
-
4. **
|
|
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:
|
|
4
|
-
description: Environment variable hygiene, secret detection, rotation, and secret-store patterns. Invoke whenever .env, secrets, API keys,
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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:
|
|
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.**
|