vanara 0.1.0 → 0.2.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/README.md +14 -0
- package/bin/vanara.js +66 -2
- package/catalog/agents/api-designer/AGENT.md +140 -0
- package/catalog/agents/api-designer/examples/openapi-snippet.yaml +130 -0
- package/catalog/agents/api-designer/examples/review-notes.md +61 -0
- package/catalog/agents/api-designer/references/contract-and-openapi.md +82 -0
- package/catalog/agents/api-designer/references/design-checklist.md +68 -0
- package/catalog/agents/api-designer/references/versioning-and-evolution.md +63 -0
- package/catalog/agents/api-designer/scripts/lint-openapi.mjs +180 -0
- package/catalog/agents/code-reviewer/AGENT.md +168 -0
- package/catalog/agents/code-reviewer/examples/pr-comment-template.md +48 -0
- package/catalog/agents/code-reviewer/examples/sample-review-output.md +38 -0
- package/catalog/agents/code-reviewer/references/review-checklist.md +46 -0
- package/catalog/agents/code-reviewer/references/security-review.md +62 -0
- package/catalog/agents/code-reviewer/references/severity-rubric.md +63 -0
- package/catalog/agents/code-reviewer/scripts/review-guard.mjs +102 -0
- package/catalog/agents/debugger/AGENT.md +112 -0
- package/catalog/agents/debugger/examples/postmortem-template.md +59 -0
- package/catalog/agents/debugger/examples/repro-template.md +50 -0
- package/catalog/agents/debugger/references/bisection.md +63 -0
- package/catalog/agents/debugger/references/debugging-method.md +57 -0
- package/catalog/agents/debugger/references/observability.md +61 -0
- package/catalog/agents/debugger/scripts/parse-stacktrace.mjs +109 -0
- package/catalog/agents/pr-summarizer/AGENT.md +156 -0
- package/catalog/agents/pr-summarizer/examples/sample-pr-summary.md +46 -0
- package/catalog/agents/pr-summarizer/examples/weak-vs-strong-summary.md +68 -0
- package/catalog/agents/pr-summarizer/references/identifying-risk.md +68 -0
- package/catalog/agents/pr-summarizer/references/reading-the-diff.md +68 -0
- package/catalog/agents/pr-summarizer/references/test-plan-and-missing-tests.md +64 -0
- package/catalog/agents/pr-summarizer/references/writing-the-summary.md +63 -0
- package/catalog/agents/pr-summarizer/scripts/diff-risk.mjs +111 -0
- package/catalog/agents/refactoring-specialist/AGENT.md +185 -0
- package/catalog/agents/refactoring-specialist/examples/before-after.md +91 -0
- package/catalog/agents/refactoring-specialist/examples/refactor-plan.md +97 -0
- package/catalog/agents/refactoring-specialist/references/large-scale-refactor.md +50 -0
- package/catalog/agents/refactoring-specialist/references/safe-workflow.md +75 -0
- package/catalog/agents/refactoring-specialist/references/smell-catalog.md +76 -0
- package/catalog/agents/refactoring-specialist/scripts/detect-smells.mjs +150 -0
- package/catalog/agents/security-auditor/AGENT.md +168 -0
- package/catalog/agents/security-auditor/examples/audit-report.md +68 -0
- package/catalog/agents/security-auditor/examples/finding-template.md +31 -0
- package/catalog/agents/security-auditor/references/audit-workflow.md +116 -0
- package/catalog/agents/security-auditor/references/severity-and-reporting.md +56 -0
- package/catalog/agents/security-auditor/references/vuln-classes.md +129 -0
- package/catalog/agents/security-auditor/scripts/scan-secrets.mjs +86 -0
- package/catalog/agents/technical-writer/AGENT.md +163 -0
- package/catalog/agents/technical-writer/examples/how-to-example.md +76 -0
- package/catalog/agents/technical-writer/examples/reference-example.md +75 -0
- package/catalog/agents/technical-writer/references/clarity-and-style.md +52 -0
- package/catalog/agents/technical-writer/references/diataxis-and-doc-types.md +61 -0
- package/catalog/agents/technical-writer/references/structure-and-maintenance.md +59 -0
- package/catalog/agents/technical-writer/scripts/readability.mjs +103 -0
- package/catalog/agents/test-author/AGENT.md +177 -0
- package/catalog/agents/test-author/examples/test-plan.md +54 -0
- package/catalog/agents/test-author/examples/unit-test-example.ts +55 -0
- package/catalog/agents/test-author/references/aaa-and-naming.md +75 -0
- package/catalog/agents/test-author/references/mocking-and-fixtures.md +94 -0
- package/catalog/agents/test-author/references/test-types-and-pyramid.md +60 -0
- package/catalog/agents/test-author/scripts/check-coverage.mjs +145 -0
- package/catalog/agents/threat-modeler/AGENT.md +163 -0
- package/catalog/agents/threat-modeler/examples/dfd-example.md +55 -0
- package/catalog/agents/threat-modeler/examples/threat-model.md +81 -0
- package/catalog/agents/threat-modeler/references/dfd-and-trust-boundaries.md +76 -0
- package/catalog/agents/threat-modeler/references/mitigation-catalog.md +77 -0
- package/catalog/agents/threat-modeler/references/stride-method.md +68 -0
- package/catalog/agents/threat-modeler/scripts/stride-checklist.mjs +183 -0
- package/catalog/agents/vuln-scanner/AGENT.md +146 -0
- package/catalog/agents/vuln-scanner/examples/finding-template.md +57 -0
- package/catalog/agents/vuln-scanner/examples/scan-report.md +49 -0
- package/catalog/agents/vuln-scanner/references/remediation-and-severity.md +68 -0
- package/catalog/agents/vuln-scanner/references/scan-types-and-tools.md +83 -0
- package/catalog/agents/vuln-scanner/references/triage-and-false-positives.md +73 -0
- package/catalog/agents/vuln-scanner/scripts/parse-scan-results.mjs +133 -0
- package/catalog/packs/code-review-pack/PACK.md +100 -0
- package/catalog/packs/code-review-pack/examples/pr-flow-example.md +50 -0
- package/catalog/packs/code-review-pack/references/rollout-guide.md +40 -0
- package/catalog/packs/security-pack/PACK.md +128 -0
- package/catalog/packs/security-pack/examples/owasp-vuln-triage-and-fix.md +66 -0
- package/catalog/packs/security-pack/examples/threat-model-then-audit.md +77 -0
- package/catalog/packs/security-pack/references/choosing-the-right-tool.md +37 -0
- package/catalog/packs/security-pack/references/threat-model-to-remediation.md +69 -0
- package/catalog/packs/security-pack/references/workflow.md +56 -0
- package/catalog/skills/api-pagination/SKILL.md +83 -0
- package/catalog/skills/api-pagination/examples/keyset-queries.sql +38 -0
- package/catalog/skills/api-pagination/examples/response-shapes.md +51 -0
- package/catalog/skills/api-pagination/references/consuming-pages.md +39 -0
- package/catalog/skills/api-pagination/references/counting-and-totals.md +41 -0
- package/catalog/skills/api-pagination/references/cursor-encoding.md +34 -0
- package/catalog/skills/api-pagination/scripts/check-cursor.mjs +51 -0
- package/catalog/skills/caching-strategies/SKILL.md +180 -0
- package/catalog/skills/caching-strategies/examples/http-cache-headers.md +82 -0
- package/catalog/skills/caching-strategies/examples/redis-cache-aside.js +110 -0
- package/catalog/skills/caching-strategies/references/cache-patterns.md +93 -0
- package/catalog/skills/caching-strategies/references/eviction-and-ttl.md +67 -0
- package/catalog/skills/caching-strategies/references/invalidation-and-stampede.md +99 -0
- package/catalog/skills/caching-strategies/scripts/ttl-jitter.mjs +99 -0
- package/catalog/skills/conventional-commits/SKILL.md +135 -0
- package/catalog/skills/conventional-commits/examples/commit-examples.md +60 -0
- package/catalog/skills/conventional-commits/examples/commitlint.config.js +31 -0
- package/catalog/skills/conventional-commits/references/breaking-changes-semver.md +50 -0
- package/catalog/skills/conventional-commits/references/scoping-commits.md +42 -0
- package/catalog/skills/conventional-commits/references/spec.md +78 -0
- package/catalog/skills/conventional-commits/scripts/lint-commit.mjs +97 -0
- package/catalog/skills/database-migrations/SKILL.md +137 -0
- package/catalog/skills/database-migrations/examples/add-column-safe.sql +34 -0
- package/catalog/skills/database-migrations/examples/migration-config.example.json +27 -0
- package/catalog/skills/database-migrations/references/expand-contract.md +74 -0
- package/catalog/skills/database-migrations/references/rollback-and-safety.md +71 -0
- package/catalog/skills/database-migrations/references/zero-downtime-changes.md +84 -0
- package/catalog/skills/database-migrations/scripts/check-migration-reversible.mjs +139 -0
- package/catalog/skills/error-handling-patterns/SKILL.md +136 -0
- package/catalog/skills/error-handling-patterns/examples/express-error-middleware.js +93 -0
- package/catalog/skills/error-handling-patterns/examples/result-pattern.ts +81 -0
- package/catalog/skills/error-handling-patterns/references/error-taxonomy.md +91 -0
- package/catalog/skills/error-handling-patterns/references/language-patterns.md +102 -0
- package/catalog/skills/error-handling-patterns/references/retry-and-backoff.md +76 -0
- package/catalog/skills/error-handling-patterns/scripts/lint-empty-catch.mjs +113 -0
- package/catalog/skills/git-collaboration-workflows/SKILL.md +104 -0
- package/catalog/skills/git-collaboration-workflows/examples/branch-protection.md +48 -0
- package/catalog/skills/git-collaboration-workflows/examples/pr-review-playbook.md +48 -0
- package/catalog/skills/git-collaboration-workflows/references/branching-models.md +52 -0
- package/catalog/skills/git-collaboration-workflows/references/history-hygiene.md +44 -0
- package/catalog/skills/git-collaboration-workflows/scripts/check-branch-hygiene.mjs +62 -0
- package/catalog/skills/owasp-top10/SKILL.md +112 -0
- package/catalog/skills/owasp-top10/examples/sql-injection-fix.md +33 -0
- package/catalog/skills/owasp-top10/examples/xss-fix.md +27 -0
- package/catalog/skills/owasp-top10/references/access-control.md +36 -0
- package/catalog/skills/owasp-top10/references/crypto-failures.md +29 -0
- package/catalog/skills/owasp-top10/references/injection.md +39 -0
- package/catalog/skills/owasp-top10/references/ssrf-and-design.md +35 -0
- package/catalog/skills/owasp-top10/scripts/scan-injection.mjs +73 -0
- package/catalog/skills/prompt-engineering/SKILL.md +100 -0
- package/catalog/skills/prompt-engineering/examples/classification-prompt.md +29 -0
- package/catalog/skills/prompt-engineering/examples/extraction-prompt.md +25 -0
- package/catalog/skills/prompt-engineering/references/prompt-injection.md +34 -0
- package/catalog/skills/prompt-engineering/references/structured-output.md +34 -0
- package/catalog/skills/prompt-engineering/references/techniques.md +33 -0
- package/catalog/skills/prompt-engineering/scripts/validate-output.mjs +71 -0
- package/catalog/skills/readme-writing/SKILL.md +176 -0
- package/catalog/skills/readme-writing/examples/README.template.md +73 -0
- package/catalog/skills/readme-writing/examples/good-readme-example.md +95 -0
- package/catalog/skills/readme-writing/references/anatomy.md +107 -0
- package/catalog/skills/readme-writing/references/maintenance.md +68 -0
- package/catalog/skills/readme-writing/references/writing-style.md +79 -0
- package/catalog/skills/readme-writing/scripts/lint-readme.mjs +117 -0
- package/catalog/skills/refactoring-patterns/SKILL.md +178 -0
- package/catalog/skills/refactoring-patterns/examples/extract-function.before-after.js +57 -0
- package/catalog/skills/refactoring-patterns/examples/replace-conditional-with-polymorphism.before-after.ts +63 -0
- package/catalog/skills/refactoring-patterns/references/code-smells.md +92 -0
- package/catalog/skills/refactoring-patterns/references/refactoring-catalog.md +78 -0
- package/catalog/skills/refactoring-patterns/references/safe-workflow.md +74 -0
- package/catalog/skills/refactoring-patterns/scripts/detect-smells.mjs +137 -0
- package/catalog/skills/rest-api-design/SKILL.md +147 -0
- package/catalog/skills/rest-api-design/examples/error-envelope.json +26 -0
- package/catalog/skills/rest-api-design/examples/orders-api.openapi.yaml +106 -0
- package/catalog/skills/rest-api-design/references/error-handling.md +43 -0
- package/catalog/skills/rest-api-design/references/pagination.md +41 -0
- package/catalog/skills/rest-api-design/references/status-codes.md +32 -0
- package/catalog/skills/rest-api-design/references/versioning.md +31 -0
- package/catalog/skills/rest-api-design/scripts/check-envelope.mjs +62 -0
- package/catalog/skills/secrets-management/SKILL.md +134 -0
- package/catalog/skills/secrets-management/examples/env-example.md +41 -0
- package/catalog/skills/secrets-management/examples/rotation-runbook.md +50 -0
- package/catalog/skills/secrets-management/references/leak-response.md +47 -0
- package/catalog/skills/secrets-management/references/rotation.md +46 -0
- package/catalog/skills/secrets-management/references/secret-managers.md +52 -0
- package/catalog/skills/secrets-management/scripts/detect-hardcoded.mjs +96 -0
- package/catalog/skills/secure-auth/SKILL.md +135 -0
- package/catalog/skills/secure-auth/examples/auth-flow.md +60 -0
- package/catalog/skills/secure-auth/examples/jwt-verify.ts +89 -0
- package/catalog/skills/secure-auth/references/auth-attacks.md +76 -0
- package/catalog/skills/secure-auth/references/oauth2-oidc.md +65 -0
- package/catalog/skills/secure-auth/references/password-hashing.md +70 -0
- package/catalog/skills/secure-auth/references/sessions-vs-jwt.md +69 -0
- package/catalog/skills/secure-auth/scripts/check-password-policy.mjs +114 -0
- package/catalog/skills/sql-index-tuning/SKILL.md +171 -0
- package/catalog/skills/sql-index-tuning/examples/explain-walkthrough.sql +91 -0
- package/catalog/skills/sql-index-tuning/examples/index-ddl.sql +67 -0
- package/catalog/skills/sql-index-tuning/references/btree-internals.md +58 -0
- package/catalog/skills/sql-index-tuning/references/composite-and-covering.md +60 -0
- package/catalog/skills/sql-index-tuning/references/reading-explain.md +54 -0
- package/catalog/skills/sql-index-tuning/references/selectivity-and-cardinality.md +62 -0
- package/catalog/skills/sql-index-tuning/scripts/suggest-index.mjs +213 -0
- package/catalog/skills/test-plan-design/SKILL.md +154 -0
- package/catalog/skills/test-plan-design/examples/test-case-template.md +45 -0
- package/catalog/skills/test-plan-design/examples/test-plan.md +57 -0
- package/catalog/skills/test-plan-design/references/case-design.md +79 -0
- package/catalog/skills/test-plan-design/references/risk-prioritization.md +58 -0
- package/catalog/skills/test-plan-design/references/test-pyramid.md +58 -0
- package/catalog/skills/test-plan-design/scripts/coverage-gaps.mjs +109 -0
- package/free-tier.json +15 -0
- package/package.json +15 -4
- package/premium-index.json +1 -0
- package/src/config.js +31 -1
- package/src/license.js +143 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Triage and False Positives
|
|
2
|
+
|
|
3
|
+
Triage is the difference between a security agent and a `grep` wrapper. Raw scanner output is a starting
|
|
4
|
+
point with a high noise floor; this reference is how you turn it into signal.
|
|
5
|
+
|
|
6
|
+
## The triage pipeline
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
raw findings
|
|
10
|
+
-> normalize (one schema: id, package, severity, location, source-tool)
|
|
11
|
+
-> dedupe (same CVE from SCA + container scan = one finding)
|
|
12
|
+
-> false-positive filter (suppress with a recorded reason)
|
|
13
|
+
-> reachability assessment (is the vulnerable path used/exposed?)
|
|
14
|
+
-> final severity (CVSS adjusted by reachability + impact)
|
|
15
|
+
-> rank + bucket (fix now / plan / accept-monitor)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The bundled `scripts/parse-scan-results.mjs` automates normalize + dedupe + severity sort. Human
|
|
19
|
+
judgment owns the false-positive and reachability steps.
|
|
20
|
+
|
|
21
|
+
## Deduplication
|
|
22
|
+
|
|
23
|
+
The same underlying CVE often appears from multiple tools and multiple dependency paths. Collapse them:
|
|
24
|
+
|
|
25
|
+
- Key on the advisory ID (CVE / GHSA) **plus** the affected package+version.
|
|
26
|
+
- Keep the highest-confidence source and union the locations.
|
|
27
|
+
- Count once in the summary. Reporting one CVE five times manufactures fake urgency and is a form of
|
|
28
|
+
alert fatigue.
|
|
29
|
+
|
|
30
|
+
## Identifying false positives
|
|
31
|
+
|
|
32
|
+
Common false positives and how to confirm them:
|
|
33
|
+
|
|
34
|
+
| Pattern | Why it's flagged | How to confirm it's benign |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| Example/placeholder secret | Matches a key regex | Value is `AKIAEXAMPLE…`, in `*.example`, or in test fixtures |
|
|
37
|
+
| Vendored test data | CVE in a bundled sample | Path is under `test/`, `fixtures/`, `__mocks__/` and not shipped |
|
|
38
|
+
| Unreachable CVE | Vulnerable function never called | `grep` for the import/call; if absent, code path is dead |
|
|
39
|
+
| Dev-only dependency | CVE in a build/test tool | Not in production bundle; lower severity, not zero |
|
|
40
|
+
| Feature-gated advisory | Applies only with a flag on | Confirm the flag/default; note the qualifier |
|
|
41
|
+
|
|
42
|
+
**Always record the suppression reason.** A silently dropped finding is indistinguishable from a missed
|
|
43
|
+
one. Put suppressed items in an "Accepted / suppressed" section with their justification.
|
|
44
|
+
|
|
45
|
+
## Reachability — the multiplier
|
|
46
|
+
|
|
47
|
+
Reachability is what separates theoretical from exploitable risk:
|
|
48
|
+
|
|
49
|
+
- **Exposed + reachable** (e.g. a deserialization CVE on a public POST handler): keep or raise severity.
|
|
50
|
+
- **Present but unreachable** (vulnerable parser never invoked): downgrade, schedule, don't block.
|
|
51
|
+
- **Dev/build-time only:** real but lower; an attacker needs supply-chain or CI access to exploit.
|
|
52
|
+
|
|
53
|
+
State the reasoning explicitly so a reviewer can overrule:
|
|
54
|
+
|
|
55
|
+
> CVE-2025-XXXX (CVSS 9.8) in `fast-xml@3.1.0`. The vulnerable `parseAttrs` path is only hit when
|
|
56
|
+
> `allowAttributes:true`; this app calls the parser with defaults. **Downgraded critical -> medium**,
|
|
57
|
+
> scheduled, not release-blocking.
|
|
58
|
+
|
|
59
|
+
## Avoiding alert fatigue
|
|
60
|
+
|
|
61
|
+
- Hard-rank. The top finding must be the genuinely most urgent, not the highest raw CVSS.
|
|
62
|
+
- Bucket aggressively: *fix now* should be short (single digits ideally). Everything else goes to
|
|
63
|
+
*plan* or *accept/monitor*.
|
|
64
|
+
- Push low/info findings into an appendix. Do not make a reader scroll past 200 lows to find the one
|
|
65
|
+
critical — that is how real issues get missed.
|
|
66
|
+
- Be consistent run-to-run so developers can diff reports and see what's new.
|
|
67
|
+
|
|
68
|
+
## When triage is genuinely uncertain
|
|
69
|
+
|
|
70
|
+
If you cannot determine reachability from static analysis alone, say so and assign provisional severity
|
|
71
|
+
on the conservative side, with a note on what would resolve the uncertainty (e.g. "needs DAST against
|
|
72
|
+
staging" or "confirm whether `module X` is in the production bundle"). Honest uncertainty beats false
|
|
73
|
+
confidence in either direction.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Normalizes a list of raw vulnerability findings: dedupes and sorts by
|
|
3
|
+
// triaged severity. Demonstrates the triage step (raw output -> ranked list)
|
|
4
|
+
// from this agent package. Zero dependencies; Node built-ins only.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// node parse-scan-results.mjs findings.json # read a JSON array from a file
|
|
8
|
+
// cat findings.json | node parse-scan-results.mjs # read from stdin
|
|
9
|
+
// node parse-scan-results.mjs --selftest # run built-in tests (exit 0/1)
|
|
10
|
+
//
|
|
11
|
+
// Input: JSON array of findings, each with at least { id, severity }.
|
|
12
|
+
// Optional fields: package, location, cvss, reachability, fix, bucket.
|
|
13
|
+
// Output: { total, deduped, bySeverity, findings } sorted critical -> info.
|
|
14
|
+
|
|
15
|
+
const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low', 'info'];
|
|
16
|
+
const SEVERITY_RANK = Object.fromEntries(SEVERITY_ORDER.map((s, i) => [s, i]));
|
|
17
|
+
|
|
18
|
+
function normalizeSeverity(sev) {
|
|
19
|
+
const s = String(sev ?? '').toLowerCase().trim();
|
|
20
|
+
return SEVERITY_RANK[s] !== undefined ? s : 'info';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Dedup key: advisory id + affected package/location. The same CVE reported by
|
|
24
|
+
// multiple tools or via multiple paths collapses to one finding.
|
|
25
|
+
function dedupKey(f) {
|
|
26
|
+
const id = String(f.id ?? 'unknown').toLowerCase();
|
|
27
|
+
const where = String(f.package ?? f.location ?? '').toLowerCase();
|
|
28
|
+
return `${id}::${where}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function parseFindings(rawList) {
|
|
32
|
+
if (!Array.isArray(rawList)) {
|
|
33
|
+
throw new Error('input must be a JSON array of findings');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const seen = new Map();
|
|
37
|
+
for (const raw of rawList) {
|
|
38
|
+
const finding = { ...raw, severity: normalizeSeverity(raw.severity) };
|
|
39
|
+
const key = dedupKey(finding);
|
|
40
|
+
if (!seen.has(key)) {
|
|
41
|
+
seen.set(key, finding);
|
|
42
|
+
} else {
|
|
43
|
+
// Keep the higher severity when duplicates disagree.
|
|
44
|
+
const existing = seen.get(key);
|
|
45
|
+
if (SEVERITY_RANK[finding.severity] < SEVERITY_RANK[existing.severity]) {
|
|
46
|
+
seen.set(key, finding);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const findings = [...seen.values()].sort((a, b) => {
|
|
52
|
+
const d = SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
|
|
53
|
+
if (d !== 0) return d;
|
|
54
|
+
return String(a.id ?? '').localeCompare(String(b.id ?? ''));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const bySeverity = Object.fromEntries(SEVERITY_ORDER.map((s) => [s, 0]));
|
|
58
|
+
for (const f of findings) bySeverity[f.severity] += 1;
|
|
59
|
+
|
|
60
|
+
return { total: rawList.length, deduped: findings.length, bySeverity, findings };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function run(json) {
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(json);
|
|
67
|
+
} catch {
|
|
68
|
+
console.error('✗ invalid JSON input');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const result = parseFindings(parsed);
|
|
73
|
+
console.log(JSON.stringify(result, null, 2));
|
|
74
|
+
process.exit(0);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error(`✗ ${err.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function selftest() {
|
|
82
|
+
const raw = [
|
|
83
|
+
{ id: 'CVE-1', package: 'lodash@4.17.15', severity: 'high' },
|
|
84
|
+
{ id: 'CVE-1', package: 'lodash@4.17.15', severity: 'medium' }, // dup, lower sev
|
|
85
|
+
{ id: 'CVE-2', package: 'axios@0.21.0', severity: 'CRITICAL' }, // case-insensitive
|
|
86
|
+
{ id: 'CVE-3', package: 'jest@29', severity: 'low' },
|
|
87
|
+
{ id: 'SECRET-1', location: 'config/x.env', severity: 'critical' },
|
|
88
|
+
{ id: 'CVE-4', package: 'debug@4', severity: 'weird' }, // -> info
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const result = parseFindings(raw);
|
|
92
|
+
const checks = [];
|
|
93
|
+
|
|
94
|
+
// Dedup: 6 raw -> 5 unique (the two CVE-1 collapse).
|
|
95
|
+
checks.push(['dedupes duplicates', result.total === 6 && result.deduped === 5]);
|
|
96
|
+
|
|
97
|
+
// Duplicate keeps the higher severity (high, not medium).
|
|
98
|
+
const cve1 = result.findings.find((f) => f.id === 'CVE-1');
|
|
99
|
+
checks.push(['dup keeps higher severity', cve1 && cve1.severity === 'high']);
|
|
100
|
+
|
|
101
|
+
// Severity counts after normalization.
|
|
102
|
+
checks.push(['critical count is 2', result.bySeverity.critical === 2]);
|
|
103
|
+
checks.push(['high count is 1', result.bySeverity.high === 1]);
|
|
104
|
+
checks.push(['unknown severity -> info', result.bySeverity.info === 1]);
|
|
105
|
+
|
|
106
|
+
// Sort order: criticals first, info last.
|
|
107
|
+
const order = result.findings.map((f) => f.severity);
|
|
108
|
+
const sorted = [...order].sort(
|
|
109
|
+
(a, b) => SEVERITY_RANK[a] - SEVERITY_RANK[b]
|
|
110
|
+
);
|
|
111
|
+
checks.push(['sorted by severity', JSON.stringify(order) === JSON.stringify(sorted)]);
|
|
112
|
+
checks.push(['first finding is critical', order[0] === 'critical']);
|
|
113
|
+
checks.push(['last finding is info', order[order.length - 1] === 'info']);
|
|
114
|
+
|
|
115
|
+
let ok = true;
|
|
116
|
+
for (const [label, passed] of checks) {
|
|
117
|
+
console.log(`${passed ? '✓' : '✗'} ${label}`);
|
|
118
|
+
ok &&= passed;
|
|
119
|
+
}
|
|
120
|
+
process.exit(ok ? 0 : 1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const arg = process.argv[2];
|
|
124
|
+
if (arg === '--selftest') {
|
|
125
|
+
selftest();
|
|
126
|
+
} else if (arg) {
|
|
127
|
+
const { readFileSync } = await import('node:fs');
|
|
128
|
+
run(readFileSync(arg, 'utf8'));
|
|
129
|
+
} else {
|
|
130
|
+
let buf = '';
|
|
131
|
+
process.stdin.on('data', (c) => (buf += c));
|
|
132
|
+
process.stdin.on('end', () => run(buf));
|
|
133
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review-pack
|
|
3
|
+
description: Review pull requests faster and better — automated first-pass review, PR summaries reviewers can trust, healthy git workflow settings, conventional history, and query-performance checks, sequenced into one review pipeline.
|
|
4
|
+
type: pack
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
updated: 2026-07-10
|
|
7
|
+
agents: [code-reviewer, pr-summarizer]
|
|
8
|
+
skills: [git-collaboration-workflows, conventional-commits, sql-index-tuning]
|
|
9
|
+
---
|
|
10
|
+
# Code Review Pack
|
|
11
|
+
|
|
12
|
+
The review toolkit for teams whose bottleneck is the pull-request queue: get every PR a
|
|
13
|
+
fast, rigorous first pass, give human reviewers a summary they can trust, and keep the
|
|
14
|
+
workflow settings and history hygiene that make review scale. The value of the pack is the
|
|
15
|
+
**sequence** — which item runs at which moment of a PR's life — not the item list.
|
|
16
|
+
|
|
17
|
+
## Who this is for
|
|
18
|
+
|
|
19
|
+
Teams merging more PRs than their senior reviewers can deeply read; leads who want review
|
|
20
|
+
latency down without review quality collapsing; anyone inheriting a repo where "review"
|
|
21
|
+
means LGTM-stamping.
|
|
22
|
+
|
|
23
|
+
## What's included
|
|
24
|
+
|
|
25
|
+
- **Agents:** `code-reviewer` (rigorous first-pass review: correctness, security, tests,
|
|
26
|
+
maintainability), `pr-summarizer` (what changed, why, risk areas — the reviewer's map).
|
|
27
|
+
- **Skills:** `git-collaboration-workflows` (branch protection, PR sizing, merge strategy,
|
|
28
|
+
monorepo questions), `conventional-commits` (machine-readable history that powers
|
|
29
|
+
changelogs and bisect), `sql-index-tuning` (the query-performance review lens most teams
|
|
30
|
+
lack — N+1s and missing indexes are the top silent regressions PRs ship).
|
|
31
|
+
|
|
32
|
+
## The review pipeline (how the pieces sequence)
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
PR opened
|
|
36
|
+
1. pr-summarizer → posts the map: what changed, why, blast radius, files to read first
|
|
37
|
+
2. code-reviewer → first-pass findings: blocking / question / nit, with file:line
|
|
38
|
+
└─ SQL touched? → sql-index-tuning lens: EXPLAIN the new queries, check indexes
|
|
39
|
+
3. human reviewer → reads the summary + findings, spends attention ONLY on judgment
|
|
40
|
+
calls the machine flagged and the design questions it can't make
|
|
41
|
+
merge
|
|
42
|
+
4. conventional-commits → squash message feeds changelog + bisect-friendly history
|
|
43
|
+
weekly
|
|
44
|
+
5. git-collaboration-workflows → hygiene audit: branch age, PR size trend, protection drift
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The division of labor is the point: agents do coverage (every line, every time), humans do
|
|
48
|
+
judgment (architecture, product fit, taste). Teams that flip this — humans doing coverage,
|
|
49
|
+
no machine pass — get slow reviews AND missed bugs.
|
|
50
|
+
|
|
51
|
+
## Setup
|
|
52
|
+
|
|
53
|
+
1. Install the pack; wire `pr-summarizer` + `code-reviewer` to run on PR-open (CI job or
|
|
54
|
+
scheduled agent).
|
|
55
|
+
2. Apply the branch-protection settings from `git-collaboration-workflows`
|
|
56
|
+
(`examples/branch-protection.md`) — required checks, stale-review dismissal, squash-only.
|
|
57
|
+
3. Adopt the `blocking:/q:/nit:` comment convention from the PR review playbook; announce
|
|
58
|
+
the review SLO (first response < 4h).
|
|
59
|
+
4. Enable commit-lint per `conventional-commits` so the history contract is enforced, not
|
|
60
|
+
hoped for.
|
|
61
|
+
|
|
62
|
+
## Comment convention (adopt verbatim)
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
blocking: must be fixed before merge — correctness, security, data loss
|
|
66
|
+
q: genuine question; answer may resolve it
|
|
67
|
+
nit: author's call; NEVER blocks
|
|
68
|
+
(unprefixed comments read as blocking — prefix everything)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Pitfalls
|
|
72
|
+
|
|
73
|
+
- **LGTM-stamping past machine findings** — the pipeline's failure mode; blocking findings
|
|
74
|
+
must actually block, or reverts won't move.
|
|
75
|
+
- **Bot-nit fatigue** — untuned severity turns the first-pass reviewer into noise; demote
|
|
76
|
+
misfiring rules weekly during rollout (see `references/rollout-guide.md`).
|
|
77
|
+
- **Summaries as gospel on giant PRs** — a wrong map on a 2,000-line PR is an argument for
|
|
78
|
+
the size norms, not against the mapper.
|
|
79
|
+
- **Skipping the SQL lens** — missing-index regressions pass every test and fail at the
|
|
80
|
+
10× tenant; the `EXPLAIN` step is not optional when queries change.
|
|
81
|
+
- **Anti-pattern: humans doing coverage** — re-reading every line the machine read wastes
|
|
82
|
+
the scarce resource (judgment) on the abundant one (attention).
|
|
83
|
+
|
|
84
|
+
## When NOT to use this shape
|
|
85
|
+
|
|
86
|
+
Solo projects (review theater), and rubber-stamp compliance environments where findings
|
|
87
|
+
can't block merges anyway — fix the process authority first, then install tooling.
|
|
88
|
+
Edge case worth naming: generated code and vendored files should be excluded from both
|
|
89
|
+
summary and review scope up front, or they drown every real finding.
|
|
90
|
+
|
|
91
|
+
## Verification
|
|
92
|
+
|
|
93
|
+
After two weeks, three numbers should move: median time-to-first-review (down), PR size p50
|
|
94
|
+
(down — the summary+size norms push authors to split), and post-merge defect
|
|
95
|
+
reverts (down). If reverts didn't move, check whether `code-reviewer` findings are being
|
|
96
|
+
LGTM'd past — the pipeline only works when blocking findings block. A full worked PR
|
|
97
|
+
walkthrough lives in [examples/pr-flow-example.md](examples/pr-flow-example.md).
|
|
98
|
+
|
|
99
|
+
Pairs with the `secure-delivery-pack` (security-focused review lenses) and
|
|
100
|
+
`craftsmanship-pack` (refactoring and test depth beyond the review gate).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Worked Example — one PR through the pipeline
|
|
2
|
+
|
|
3
|
+
PR #512: "Add invoice CSV export" — 380 lines, touches API + a new query + one UI table.
|
|
4
|
+
|
|
5
|
+
## 1. pr-summarizer posts (2 min after open)
|
|
6
|
+
|
|
7
|
+
> **What**: New `GET /invoices/export.csv` endpoint (streaming), export button on the
|
|
8
|
+
> invoices table, `invoices_export` permission.
|
|
9
|
+
> **Why**: FEAT-201 — finance team needs monthly exports.
|
|
10
|
+
> **Read first**: `export-controller.ts` (new query!), `permissions.ts` (auth change).
|
|
11
|
+
> **Blast radius**: read-only endpoint; new DB query on `invoices` (potentially large);
|
|
12
|
+
> no schema change. **Risk**: memory on large exports, query performance.
|
|
13
|
+
|
|
14
|
+
## 2. code-reviewer posts (5 min after open)
|
|
15
|
+
|
|
16
|
+
> `blocking:` export-controller.ts:41 — query selects `*` and loads all rows into memory
|
|
17
|
+
> before streaming; a 500k-invoice tenant OOMs the pod. Stream with a cursor
|
|
18
|
+
> (keyset pagination) or use the DB driver's row stream.
|
|
19
|
+
>
|
|
20
|
+
> `blocking:` export-query.sql — `WHERE tenant_id = ? AND created_at > ?` has no
|
|
21
|
+
> supporting composite index (per sql-index-tuning: needs `(tenant_id, created_at)`;
|
|
22
|
+
> current plan is a seq scan at 2M rows — EXPLAIN attached).
|
|
23
|
+
>
|
|
24
|
+
> `q:` permissions.ts:88 — `invoices_export` granted to `member` by default; FEAT-201
|
|
25
|
+
> says finance-only. Intended?
|
|
26
|
+
>
|
|
27
|
+
> `nit:` csv column headers hardcoded English — project is i18n'd (`internationalization`).
|
|
28
|
+
|
|
29
|
+
## 3. Human reviewer (18 min later, reads summary + findings first)
|
|
30
|
+
|
|
31
|
+
Spends attention on the two judgment calls: confirms permission should be finance-only
|
|
32
|
+
(product check), and decides streaming approach (driver row-stream over keyset — simpler
|
|
33
|
+
here). Doesn't re-read the 300 lines the machine already covered line-by-line; spot-checks
|
|
34
|
+
the test file.
|
|
35
|
+
|
|
36
|
+
## 4. Author revises → checks green → squash-merge
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
feat(invoices): add streaming CSV export (FEAT-201)
|
|
40
|
+
|
|
41
|
+
Finance-only permission; keyset-cursor streaming; composite index
|
|
42
|
+
(tenant_id, created_at) added expand-only.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Outcome ledger
|
|
46
|
+
|
|
47
|
+
- OOM prevented pre-merge (would have been a Sev-2 at month-end).
|
|
48
|
+
- Missing index caught before it met the 2M-row tenant.
|
|
49
|
+
- Human review time: ~20 min for a 380-line PR, spent entirely on judgment.
|
|
50
|
+
- History entry supports changelog + bisect.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Rollout Guide
|
|
2
|
+
|
|
3
|
+
Review culture changes fail when imposed all at once. The two-week sequence that lands:
|
|
4
|
+
|
|
5
|
+
## Week 1 — observation mode
|
|
6
|
+
|
|
7
|
+
- Wire `pr-summarizer` only. Zero friction, immediate value: every PR gets a map. Nobody
|
|
8
|
+
argues with a summary.
|
|
9
|
+
- Run `code-reviewer` in **comment-only** mode (findings posted, nothing blocks). The team
|
|
10
|
+
calibrates trust: are the findings real? (Tune severity thresholds now, while stakes are
|
|
11
|
+
low.)
|
|
12
|
+
- Baseline the three metrics: time-to-first-review, PR size p50, revert rate. You need the
|
|
13
|
+
"before" or the "after" is a vibe.
|
|
14
|
+
|
|
15
|
+
## Week 2 — the contract
|
|
16
|
+
|
|
17
|
+
- Turn on branch protection per the skill's settings (checks required, stale-dismissal,
|
|
18
|
+
squash-only). Announce it as config, with the escape hatch documented (hotfix label path).
|
|
19
|
+
- Adopt comment prefixes (`blocking:/q:/nit:`) and the 4-hour first-response SLO — put the
|
|
20
|
+
SLO on a dashboard where standup sees it.
|
|
21
|
+
- commit-lint on (warning for 3 days, then required).
|
|
22
|
+
|
|
23
|
+
## Weeks 3+ — enforcement earned
|
|
24
|
+
|
|
25
|
+
- `code-reviewer` blocking findings now actually block (the team has seen two weeks of its
|
|
26
|
+
judgment; false-positive rate is known and tuned).
|
|
27
|
+
- Weekly hygiene audit (`check-branch-hygiene.mjs`) posted to the team channel — trends,
|
|
28
|
+
not blame.
|
|
29
|
+
|
|
30
|
+
## Objection handling (you will hear these)
|
|
31
|
+
|
|
32
|
+
- **"The bot nitpicks."** Nits never block — that's what the prefix system encodes. Tune or
|
|
33
|
+
demote rules that misfire; the config is code-reviewable.
|
|
34
|
+
- **"Summaries are wrong sometimes."** They're maps, not territory — a wrong summary on a
|
|
35
|
+
2,000-line PR is an argument for smaller PRs, which the size norms fix.
|
|
36
|
+
- **"This slows us down."** The SLO + protection settings measurably speed up the queue;
|
|
37
|
+
show the week-1 baseline vs week-4. What slows teams is review latency and re-review
|
|
38
|
+
churn, both of which drop.
|
|
39
|
+
- **"Admins need bypass."** The bypass IS the vulnerability; the hotfix path exists and is
|
|
40
|
+
faster than arguing at 2 a.m.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-pack
|
|
3
|
+
description: Build and ship secure software — threat modeling at design time, OWASP code audits, dependency/secret scanning, and secure auth and secrets handling, sequenced into one shift-left workflow.
|
|
4
|
+
type: pack
|
|
5
|
+
version: 2.0.0
|
|
6
|
+
updated: 2026-07-06
|
|
7
|
+
agents: [threat-modeler, security-auditor, vuln-scanner]
|
|
8
|
+
skills: [owasp-top10, secure-auth, secrets-management]
|
|
9
|
+
---
|
|
10
|
+
# Security Pack
|
|
11
|
+
|
|
12
|
+
Security is cheapest when it moves **left** — designed in at the whiteboard, enforced while the code
|
|
13
|
+
is written, verified before merge, and monitored in the supply chain. This pack bundles three agents
|
|
14
|
+
and three skills that cover that whole arc and, more importantly, teaches **which one to reach for at
|
|
15
|
+
each stage of a feature's life**.
|
|
16
|
+
|
|
17
|
+
The value of a pack is not the list of items — it is the sequencing. A threat model with no audit is
|
|
18
|
+
a document nobody enforces; an audit with no threat model is a checklist with no context. This
|
|
19
|
+
document ties them into one repeatable loop: **model → build → audit → scan → ship**.
|
|
20
|
+
|
|
21
|
+
## Who this is for
|
|
22
|
+
|
|
23
|
+
- Security engineers who own the security posture of a product or service.
|
|
24
|
+
- Developers who own the security of the features they build — the ones expected to threat-model,
|
|
25
|
+
self-audit, and keep their dependencies clean before asking for review.
|
|
26
|
+
- Tech leads introducing a shift-left practice to a team that currently bolts security on at the end.
|
|
27
|
+
|
|
28
|
+
If you only need a one-off review of an existing diff, the `security-auditor` agent alone is enough.
|
|
29
|
+
Reach for the full pack when you are carrying a feature **from design through to a clean merge** and
|
|
30
|
+
want security enforced at every handoff instead of a single gate at the finish line.
|
|
31
|
+
|
|
32
|
+
## What's included
|
|
33
|
+
|
|
34
|
+
| Item | Kind | Job in the workflow |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `threat-modeler` | agent | STRIDE-style threat modeling at design time — enumerate attack surface, trust boundaries, and abuse cases before code exists |
|
|
37
|
+
| `security-auditor` | agent | OWASP-focused code review of the diff — injection, auth flaws, broken access control, unsafe crypto |
|
|
38
|
+
| `vuln-scanner` | agent | Dependency CVE scanning and secret detection across the tree and git history |
|
|
39
|
+
| `owasp-top10` | skill | Per-risk defenses for the OWASP Top 10 — the reference the auditor and you apply |
|
|
40
|
+
| `secure-auth` | skill | Correct password hashing, session/token handling, and MFA construction |
|
|
41
|
+
| `secrets-management` | skill | Keep secrets out of source, load them safely, and rotate without downtime |
|
|
42
|
+
|
|
43
|
+
Agents *do* the work; skills are the *reference* they (and you) apply while doing it. Pair an agent
|
|
44
|
+
with its matching skill — `security-auditor` + `owasp-top10`, or building auth with `secure-auth` —
|
|
45
|
+
so the output is both produced and grounded in a standard.
|
|
46
|
+
|
|
47
|
+
## The end-to-end workflow
|
|
48
|
+
|
|
49
|
+
A secure feature moves through five stages. The pack maps one primary item to each:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
1. Model → threat-modeler (+ owasp-top10) enumerate threats before code
|
|
53
|
+
2. Build → secure-auth / secrets-management implement the risky parts correctly
|
|
54
|
+
3. Audit → security-auditor (+ owasp-top10) review the diff, security-first
|
|
55
|
+
4. Scan → vuln-scanner CVEs in deps + secrets in the tree
|
|
56
|
+
5. Ship → gate on: no open CRITICAL/HIGH, clean scan
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Model **first**, before any code. A threat model produced after the feature is built is a
|
|
60
|
+
rationalization, not a design tool — its whole purpose is to change what you build. The threats it
|
|
61
|
+
surfaces become the audit checklist in stage 3 and the abuse-case tests you write while building.
|
|
62
|
+
|
|
63
|
+
### Worked example: adding "password reset via email link"
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
threat-modeler → surfaces: token guessability, token reuse/replay, host-header poisoning in the
|
|
67
|
+
reset link, user enumeration on the "email sent" response, rate-limit bypass
|
|
68
|
+
owasp-top10 → maps those to A01 (access control), A07 (auth failures), A04 (insecure design)
|
|
69
|
+
secure-auth → reset token = 256-bit random, single-use, 15-min TTL, hashed at rest; response is
|
|
70
|
+
identical whether or not the email exists (no enumeration)
|
|
71
|
+
secrets-management → the email-provider API key is loaded from the secret store, never in the repo
|
|
72
|
+
security-auditor → confirms token is compared in constant time, is invalidated on use, and the
|
|
73
|
+
"sent" response leaks nothing; flags the missing rate limit as HIGH
|
|
74
|
+
vuln-scanner → the email SDK pulls a transitive dep with a known SSRF CVE — flagged, bump required
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Every item earned its place: the threat model set the agenda, the skills shaped the implementation,
|
|
78
|
+
the auditor verified it, and the scanner caught a supply-chain issue no human review would have.
|
|
79
|
+
|
|
80
|
+
## How to choose the right item
|
|
81
|
+
|
|
82
|
+
- **Designing a feature that touches auth, money, or user data?** `threat-modeler` + `owasp-top10`
|
|
83
|
+
first — enumerate abuse cases before a line is written.
|
|
84
|
+
- **Implementing login, sessions, or MFA?** `secure-auth` — do not hand-roll password hashing or
|
|
85
|
+
token generation.
|
|
86
|
+
- **About to reference a credential, key, or token?** `secrets-management` — it never belongs in
|
|
87
|
+
source, config committed to git, or a client bundle.
|
|
88
|
+
- **Diff ready for review?** `security-auditor` — security pass before correctness, every time.
|
|
89
|
+
- **Before merge, always?** `vuln-scanner` — CVEs and leaked secrets are invisible to code review.
|
|
90
|
+
|
|
91
|
+
See `references/choosing-the-right-tool.md` for the full decision table, `references/workflow.md` for
|
|
92
|
+
the stage-by-stage playbook, and `references/threat-model-to-remediation.md` for the discipline
|
|
93
|
+
playbook that connects a threat model to concrete fixes. Two complete runs are in `examples/`.
|
|
94
|
+
|
|
95
|
+
## Common pitfalls (anti-patterns this pack prevents)
|
|
96
|
+
|
|
97
|
+
- **Threat-modeling after the build.** A model written to justify existing code enumerates the
|
|
98
|
+
threats you already handled and misses the ones you didn't. The workflow puts it at stage 1 so it
|
|
99
|
+
can actually change the design.
|
|
100
|
+
- **Auditing without a threat model.** A generic OWASP checklist misses domain abuse cases (e.g. a
|
|
101
|
+
refund flow that can go negative). The threat model *is* the audit's context.
|
|
102
|
+
- **Treating secret scanning as one-time.** A secret committed once lives in git history forever even
|
|
103
|
+
after it's deleted from HEAD. `vuln-scanner` checks history, and the fix is rotation, not just
|
|
104
|
+
removal.
|
|
105
|
+
- **Rolling your own crypto/auth.** Hand-rolled password hashing, JWT verification, or token
|
|
106
|
+
comparison is a reliable source of criticals. `secure-auth` points to vetted constructions.
|
|
107
|
+
- **Fixing the secret, forgetting to rotate.** Deleting a leaked key from source does not un-leak it.
|
|
108
|
+
Any exposed secret must be rotated, not just removed.
|
|
109
|
+
|
|
110
|
+
## When NOT to use this pack
|
|
111
|
+
|
|
112
|
+
- **Compliance/audit-trail work** (GDPR, SOC 2 evidence) — that's `compliance-auditor` and the
|
|
113
|
+
privacy/audit-logging skills, not this pack's application-security focus.
|
|
114
|
+
- **Infrastructure and network hardening** — firewall rules, TLS termination, and cloud IAM are
|
|
115
|
+
DevOps/network territory, not application-code security.
|
|
116
|
+
- **A one-line, no-risk change** — a copy tweak or a CSS fix does not need a threat model; a single
|
|
117
|
+
`security-auditor` pass (or nothing) is proportionate.
|
|
118
|
+
- **Smart-contract security** — use `smart-contract-auditor` and `smart-contract-security`; the
|
|
119
|
+
threat surface there is different.
|
|
120
|
+
|
|
121
|
+
## Files in this package
|
|
122
|
+
|
|
123
|
+
- `references/workflow.md` — the five-stage security workflow in detail, with entry/exit criteria.
|
|
124
|
+
- `references/choosing-the-right-tool.md` — full decision table: symptom → item.
|
|
125
|
+
- `references/threat-model-to-remediation.md` — the discipline playbook connecting STRIDE threats to
|
|
126
|
+
audited, tested remediations.
|
|
127
|
+
- `examples/threat-model-then-audit.md` — a feature threat-modeled at design time, then audited.
|
|
128
|
+
- `examples/owasp-vuln-triage-and-fix.md` — an OWASP-class vulnerability triaged and remediated.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Walkthrough: triaging and fixing an OWASP-class vulnerability
|
|
2
|
+
|
|
3
|
+
Not every task starts at design time. This shows the pack applied to a vulnerability found in
|
|
4
|
+
existing code — where `security-auditor` leads and the workflow compresses.
|
|
5
|
+
|
|
6
|
+
## The finding
|
|
7
|
+
|
|
8
|
+
A routine `security-auditor` pass on a diff flags:
|
|
9
|
+
|
|
10
|
+
```md
|
|
11
|
+
### CRITICAL
|
|
12
|
+
- `api/documents.js:73` — GET /documents/:id loads the record by id and returns it with no check
|
|
13
|
+
that the document belongs to the requesting user. Any authenticated user can read any document by
|
|
14
|
+
guessing/enumerating ids. (A01 Broken Access Control — IDOR)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Intermittent guessing isn't needed — the ids are sequential integers, so this is trivially
|
|
18
|
+
enumerable. CRITICAL is correct.
|
|
19
|
+
|
|
20
|
+
## Classify (`owasp-top10`)
|
|
21
|
+
|
|
22
|
+
This is **A01 Broken Access Control**, the IDOR variant: the endpoint authenticates ("are you logged
|
|
23
|
+
in?") but does not authorize ("is this *your* document?"). The reference is clear — authorization must
|
|
24
|
+
be checked server-side against the resource owner on every request, deny-by-default.
|
|
25
|
+
|
|
26
|
+
## Reproduce as a failing test first (`security-auditor` direction)
|
|
27
|
+
|
|
28
|
+
Before touching the handler, lock the bug down as a regression test:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
✗ user B requesting user A's document id receives 404, not 200 + the document
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
It fails against current code — confirming the vulnerability and giving the fix a target. (404, not
|
|
35
|
+
403, so the endpoint doesn't confirm the id exists — avoids the enumeration side channel too.)
|
|
36
|
+
|
|
37
|
+
## Fix the access-control check
|
|
38
|
+
|
|
39
|
+
Add the ownership guard *before* the record is returned:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
const doc = await Documents.findById(id);
|
|
43
|
+
if (!doc || doc.ownerId !== session.userId) return res.status(404).end();
|
|
44
|
+
return res.json({ data: doc, error: null });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The check runs for every request and denies by default. Test goes green. A grep for the same pattern
|
|
48
|
+
across sibling routes (`/documents/:id/download`, `/documents/:id/versions`) confirms whether the same
|
|
49
|
+
gap exists elsewhere — IDOR is rarely a single-endpoint problem.
|
|
50
|
+
|
|
51
|
+
## Re-audit + scan (`security-auditor` + `vuln-scanner`)
|
|
52
|
+
|
|
53
|
+
- `security-auditor` re-reviews: the guard is present on all three routes, the 404 leaks nothing, no
|
|
54
|
+
new finding introduced.
|
|
55
|
+
- `vuln-scanner` runs on the branch: dependencies clean, no secret introduced by the change.
|
|
56
|
+
|
|
57
|
+
## Ship
|
|
58
|
+
|
|
59
|
+
CRITICAL closed, sibling routes patched, regression tests in place, scan clean. The fix ships.
|
|
60
|
+
|
|
61
|
+
## The lesson
|
|
62
|
+
|
|
63
|
+
The pack turned a single flagged line into a *class* fix: classify the vulnerability (A01/IDOR), lock
|
|
64
|
+
it with a failing test, apply the deny-by-default mitigation, then sweep for the same pattern
|
|
65
|
+
elsewhere. Fixing only line 73 would have left the download and versions routes exploitable — the
|
|
66
|
+
OWASP framing is what prompts checking the whole class, not just the reported instance.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Walkthrough: threat-modeling "password reset via email", then auditing it
|
|
2
|
+
|
|
3
|
+
A complete run through the five stages for a classic high-risk feature, showing which pack item leads
|
|
4
|
+
at each point.
|
|
5
|
+
|
|
6
|
+
## Stage 1 — Model (`threat-modeler` + `owasp-top10`)
|
|
7
|
+
|
|
8
|
+
Trust boundaries: browser → API → email provider → token store. Walking STRIDE surfaces:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
- Token guessability → tokens must be cryptographically random (A07)
|
|
12
|
+
- Token reuse / replay → single-use, short TTL, invalidated on use (A07)
|
|
13
|
+
- Host-header poisoning → reset link host must not come from the request header (A04)
|
|
14
|
+
- User enumeration → "email sent" response identical for known/unknown emails (A01)
|
|
15
|
+
- No rate limit → attacker can brute-force or spam resets (A04/A07)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Each threat is tagged with its OWASP class and a planned mitigation. This list becomes the audit
|
|
19
|
+
agenda.
|
|
20
|
+
|
|
21
|
+
## Stage 2 — Build (`secure-auth` + `secrets-management`)
|
|
22
|
+
|
|
23
|
+
Implement against the vetted construction:
|
|
24
|
+
|
|
25
|
+
- Reset token = 256-bit CSPRNG value, stored **hashed** at rest, single-use, 15-minute TTL.
|
|
26
|
+
- The "we sent you an email" response is byte-identical whether or not the account exists.
|
|
27
|
+
- The reset link's host is built from server config, not the incoming `Host` header.
|
|
28
|
+
- The email-provider API key is loaded from the secret store via `secrets-management` — it never
|
|
29
|
+
appears in source or committed config.
|
|
30
|
+
|
|
31
|
+
Abuse-case tests are written alongside:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
✗ a used reset token cannot be redeemed a second time
|
|
35
|
+
✗ /forgot-password returns the same body for known and unknown emails
|
|
36
|
+
✗ /forgot-password returns 429 after 5 attempts in the window
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Implement until green.
|
|
40
|
+
|
|
41
|
+
## Stage 3 — Audit (`security-auditor` + `owasp-top10`)
|
|
42
|
+
|
|
43
|
+
The auditor reviews the diff against the stage-1 list:
|
|
44
|
+
|
|
45
|
+
```md
|
|
46
|
+
### HIGH
|
|
47
|
+
- `auth/reset.js:41` — token compared with `===`; use a constant-time compare to avoid a timing
|
|
48
|
+
oracle on the token value.
|
|
49
|
+
- `auth/reset.js:12` — no rate limiter on the request-reset endpoint (threat listed in the model,
|
|
50
|
+
not yet implemented). Add per-IP + per-account throttling.
|
|
51
|
+
|
|
52
|
+
### LOW
|
|
53
|
+
- `auth/reset.js:58` — TTL is 60 min; model called for 15. Tighten.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The timing-compare and rate-limit findings are fixed and re-reviewed. No CRITICAL/HIGH remains.
|
|
57
|
+
|
|
58
|
+
## Stage 4 — Scan (`vuln-scanner`)
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
CVE: email-sdk@2.3.0 → transitive dep `node-fetch@2.6.1` has a known SSRF advisory.
|
|
62
|
+
Fix: bump to email-sdk@2.4.1 (pulls patched fetch).
|
|
63
|
+
Secrets: none in tree; git history clean.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Dependency bumped; scan re-run clean.
|
|
67
|
+
|
|
68
|
+
## Stage 5 — Ship
|
|
69
|
+
|
|
70
|
+
Audit clean, scan clean, no committed secret. The feature ships with its abuse-case tests as
|
|
71
|
+
permanent regression guards.
|
|
72
|
+
|
|
73
|
+
## The lesson
|
|
74
|
+
|
|
75
|
+
The threat model did the heavy lifting: it predicted the rate-limit gap and the enumeration risk
|
|
76
|
+
*before* code existed, and it gave the auditor a precise agenda. Without it, stage 3 would have been a
|
|
77
|
+
generic pass that could easily have missed the missing rate limiter.
|