security-mcp 1.1.3 → 1.3.1
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 +164 -185
- package/defaults/checklists/ai.json +20 -1
- package/defaults/checklists/api.json +35 -1
- package/defaults/checklists/infra.json +34 -1
- package/defaults/checklists/mobile.json +23 -1
- package/defaults/checklists/payments.json +15 -1
- package/defaults/checklists/web.json +11 -1
- package/defaults/control-catalog.json +200 -0
- package/defaults/security-policy.json +2 -2
- package/dist/cli/index.js +82 -5
- package/dist/cli/install.js +36 -6
- package/dist/cli/onboarding.js +6 -0
- package/dist/gate/baseline.js +82 -7
- package/dist/gate/catalog.js +10 -2
- package/dist/gate/checks/ai.js +757 -39
- package/dist/gate/checks/auth-deep.js +935 -0
- package/dist/gate/checks/business-logic.js +751 -0
- package/dist/gate/checks/ci-pipeline.js +399 -4
- package/dist/gate/checks/crypto.js +423 -2
- package/dist/gate/checks/dependencies.js +571 -15
- package/dist/gate/checks/graphql.js +201 -19
- package/dist/gate/checks/infra.js +246 -1
- package/dist/gate/checks/injection-deep.js +848 -0
- package/dist/gate/checks/k8s.js +114 -1
- package/dist/gate/checks/mobile-android.js +917 -3
- package/dist/gate/checks/mobile-ios.js +797 -5
- package/dist/gate/checks/required-artifacts.js +194 -0
- package/dist/gate/checks/runtime.js +178 -0
- package/dist/gate/checks/secrets.js +244 -13
- package/dist/gate/checks/supply-chain-deep.js +787 -0
- package/dist/gate/checks/web-nextjs.js +572 -48
- package/dist/gate/diff.js +17 -5
- package/dist/gate/evidence.js +8 -1
- package/dist/gate/exceptions.js +131 -9
- package/dist/gate/policy.js +282 -129
- package/dist/mcp/audit-chain.js +122 -28
- package/dist/mcp/auth.js +169 -0
- package/dist/mcp/learning.js +129 -4
- package/dist/mcp/model-router.js +158 -21
- package/dist/mcp/orchestration.js +186 -51
- package/dist/mcp/server.js +608 -94
- package/dist/repo/fs.js +24 -1
- package/dist/repo/search.js +31 -6
- package/dist/review/store.js +52 -1
- package/package.json +7 -7
- package/prompts/SECURITY_PROMPT.md +73 -0
- package/skills/_TEMPLATE/SKILL.md +99 -0
- package/skills/advanced-dos-tester/SKILL.md +109 -0
- package/skills/agentic-loop-exploiter/SKILL.md +368 -0
- package/skills/ai-llm-redteam/SKILL.md +104 -0
- package/skills/ai-model-supply-chain-agent/SKILL.md +103 -0
- package/skills/algorithm-implementation-reviewer/SKILL.md +98 -0
- package/skills/android-penetration-tester/SKILL.md +455 -46
- package/skills/anti-replay-tester/SKILL.md +106 -0
- package/skills/appsec-code-auditor/SKILL.md +120 -0
- package/skills/artifact-integrity-analyst/SKILL.md +441 -0
- package/skills/attack-navigator/SKILL.md +467 -8
- package/skills/auth-session-hacker/SKILL.md +128 -0
- package/skills/aws-penetration-tester/SKILL.md +456 -0
- package/skills/azure-penetration-tester/SKILL.md +490 -3
- package/skills/binary-auth-validator/SKILL.md +111 -0
- package/skills/bot-detection-specialist/SKILL.md +109 -0
- package/skills/business-logic-attacker/SKILL.md +231 -0
- package/skills/capec-code-mapper/SKILL.md +84 -0
- package/skills/cert-pin-rotation-specialist/SKILL.md +112 -0
- package/skills/cicd-pipeline-hijacker/SKILL.md +405 -0
- package/skills/ciso-orchestrator/SKILL.md +454 -43
- package/skills/cloud-infra-specialist/SKILL.md +118 -0
- package/skills/compliance-gap-analyst/SKILL.md +422 -0
- package/skills/compliance-grc/SKILL.md +85 -0
- package/skills/compliance-lifecycle-tracker/SKILL.md +84 -0
- package/skills/credential-stuffing-specialist/SKILL.md +102 -0
- package/skills/crypto-pki-specialist/SKILL.md +87 -0
- package/skills/csa-ccm-mapper/SKILL.md +84 -0
- package/skills/csf2-governance-mapper/SKILL.md +84 -0
- package/skills/deep-link-fuzzer/SKILL.md +109 -0
- package/skills/dependency-confusion-attacker/SKILL.md +415 -0
- package/skills/device-integrity-aggregator/SKILL.md +108 -0
- package/skills/dos-resilience-tester/SKILL.md +97 -0
- package/skills/dread-scorer/SKILL.md +84 -0
- package/skills/egress-policy-enforcer/SKILL.md +99 -0
- package/skills/evidence-collector/SKILL.md +98 -0
- package/skills/file-upload-attacker/SKILL.md +109 -0
- package/skills/gcp-penetration-tester/SKILL.md +459 -2
- package/skills/git-history-secret-scanner/SKILL.md +106 -0
- package/skills/iam-privesc-graph-builder/SKILL.md +152 -0
- package/skills/incident-responder/SKILL.md +111 -0
- package/skills/injection-specialist/SKILL.md +131 -0
- package/skills/ios-security-auditor/SKILL.md +282 -0
- package/skills/json-ambiguity-tester/SKILL.md +0 -0
- package/skills/k8s-container-escaper/SKILL.md +384 -0
- package/skills/key-management-lifecycle-analyst/SKILL.md +98 -0
- package/skills/kill-switch-engineer/SKILL.md +102 -0
- package/skills/linddun-privacy-analyst/SKILL.md +102 -0
- package/skills/logic-race-fuzzer/SKILL.md +443 -0
- package/skills/mobile-api-network-attacker/SKILL.md +421 -0
- package/skills/mobile-binary-hardener/SKILL.md +102 -0
- package/skills/mobile-security-specialist/SKILL.md +85 -0
- package/skills/mobile-webview-auditor/SKILL.md +96 -0
- package/skills/model-extraction-attacker/SKILL.md +219 -0
- package/skills/multipart-abuse-tester/SKILL.md +84 -0
- package/skills/oauth-pkce-specialist/SKILL.md +104 -0
- package/skills/parser-exhaustion-tester/SKILL.md +142 -0
- package/skills/pentest-infra/SKILL.md +141 -0
- package/skills/pentest-social/SKILL.md +201 -0
- package/skills/pentest-team/SKILL.md +134 -0
- package/skills/pentest-web-api/SKILL.md +151 -0
- package/skills/privacy-flow-analyst/SKILL.md +234 -0
- package/skills/prompt-injection-specialist/SKILL.md +394 -0
- package/skills/quantum-migration-planner/SKILL.md +96 -0
- package/skills/rag-poisoning-specialist/SKILL.md +358 -0
- package/skills/registry-mirror-enforcer/SKILL.md +84 -0
- package/skills/rotation-validation-agent/SKILL.md +112 -0
- package/skills/samm-assessor/SKILL.md +85 -0
- package/skills/secrets-mask-bypass-tester/SKILL.md +100 -0
- package/skills/senior-security-engineer/SKILL.md +370 -2
- package/skills/serialization-memory-attacker/SKILL.md +332 -0
- package/skills/session-timeout-tester/SKILL.md +161 -0
- package/skills/slsa-level3-enforcer/SKILL.md +112 -0
- package/skills/slsa-provenance-enforcer/SKILL.md +102 -0
- package/skills/ssrf-detection-validator/SKILL.md +108 -0
- package/skills/step-up-auth-enforcer/SKILL.md +84 -0
- package/skills/stride-pasta-analyst/SKILL.md +420 -0
- package/skills/supply-chain-devsecops/SKILL.md +98 -0
- package/skills/threat-infrastructure-analyst/SKILL.md +84 -0
- package/skills/threat-modeler/SKILL.md +85 -0
- package/skills/tls-certificate-auditor/SKILL.md +573 -18
- package/skills/token-reuse-detector/SKILL.md +95 -0
- package/skills/trike-risk-modeler/SKILL.md +84 -0
- package/skills/unicode-homograph-tester/SKILL.md +84 -0
- package/skills/waf-rule-lifecycle-agent/SKILL.md +97 -0
- package/skills/webhook-security-tester/SKILL.md +102 -0
- package/skills/zero-trust-architect/SKILL.md +109 -0
package/dist/repo/fs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
function getWorkspaceRoot() {
|
|
4
4
|
return process.cwd();
|
|
@@ -16,5 +16,28 @@ export async function readFileSafe(relPath) {
|
|
|
16
16
|
if (p !== root && !p.startsWith(rootPrefix)) {
|
|
17
17
|
throw new Error("Path traversal blocked");
|
|
18
18
|
}
|
|
19
|
+
// Resolve symlinks and verify the real path is also within the workspace.
|
|
20
|
+
// This prevents symlink traversal attacks where a symlink inside the workspace
|
|
21
|
+
// points to a file outside it. CWE-61 / CAPEC-132.
|
|
22
|
+
try {
|
|
23
|
+
const realResolved = await realpath(p);
|
|
24
|
+
const realRoot = await realpath(root);
|
|
25
|
+
const realRootPrefix = realRoot + path.sep;
|
|
26
|
+
if (realResolved !== realRoot && !realResolved.startsWith(realRootPrefix)) {
|
|
27
|
+
throw new Error(`Symlink traversal detected: ${relPath} -> ${realResolved}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e.code === "ENOENT") {
|
|
32
|
+
throw new Error(`File not found: ${relPath}`);
|
|
33
|
+
}
|
|
34
|
+
if (e.message.includes("Symlink traversal"))
|
|
35
|
+
throw e;
|
|
36
|
+
// SECURITY: Any other realpath error (EACCES, ELOOP, etc.) means we could not
|
|
37
|
+
// verify the real path is within the workspace. Deny rather than fall through,
|
|
38
|
+
// because readFile() would follow symlinks using the unverified lexical path,
|
|
39
|
+
// enabling traversal to out-of-workspace targets. CWE-61 / CAPEC-132.
|
|
40
|
+
throw new Error(`Cannot verify path safety for ${relPath}: ${e.message}`);
|
|
41
|
+
}
|
|
19
42
|
return await readFile(p, "utf8");
|
|
20
43
|
}
|
package/dist/repo/search.js
CHANGED
|
@@ -2,10 +2,30 @@ import fg from "fast-glob";
|
|
|
2
2
|
import { readFileSafe } from "./fs.js";
|
|
3
3
|
// Maximum allowed regex pattern length. Longer patterns significantly raise
|
|
4
4
|
// the risk of catastrophic backtracking (ReDoS). CWE-1333.
|
|
5
|
-
const MAX_REGEX_LEN =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const MAX_REGEX_LEN = 500;
|
|
6
|
+
/**
|
|
7
|
+
* Detects regex patterns that risk catastrophic backtracking (ReDoS).
|
|
8
|
+
* Covers nested quantifiers, ambiguous alternation with outer quantifiers,
|
|
9
|
+
* counted repetition inside groups, and overlapping wildcard groups.
|
|
10
|
+
* CWE-1333 / MITRE ATT&CK T1499.
|
|
11
|
+
*/
|
|
12
|
+
function isCatastrophicRegex(pattern) {
|
|
13
|
+
// Original: nested quantifiers like (a+)+, (a*)*, (\w+)+
|
|
14
|
+
if (/\([^)]*[+*][^)]*\)[+*?{]/.test(pattern))
|
|
15
|
+
return true;
|
|
16
|
+
// Ambiguous alternation with outer quantifier: (a|aa)+ or (a|b)+
|
|
17
|
+
if (/\([^)]*\|[^)]*\)[+*]/.test(pattern))
|
|
18
|
+
return true;
|
|
19
|
+
// Counted repetition with nested group: (a{2,})+ or (a{1,3})+
|
|
20
|
+
if (/\([^)]*\{[^)]*\}[^)]*\)[+*]/.test(pattern))
|
|
21
|
+
return true;
|
|
22
|
+
// Overlapping alternatives: (.+)+ or (\w+)+
|
|
23
|
+
if (/\(\.[+*][^)]*\)[+*]/.test(pattern))
|
|
24
|
+
return true;
|
|
25
|
+
if (/\(\\[wWdDsS][+*][^)]*\)[+*]/.test(pattern))
|
|
26
|
+
return true;
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
9
29
|
/**
|
|
10
30
|
* Validates and compiles a user-supplied regex string.
|
|
11
31
|
* Throws if the pattern is dangerously long, contains known ReDoS signatures,
|
|
@@ -16,12 +36,16 @@ function compileUserRegex(pattern) {
|
|
|
16
36
|
if (pattern.length > MAX_REGEX_LEN) {
|
|
17
37
|
throw new Error(`Regex pattern too long (max ${MAX_REGEX_LEN} chars)`);
|
|
18
38
|
}
|
|
19
|
-
if (
|
|
39
|
+
if (isCatastrophicRegex(pattern)) {
|
|
20
40
|
throw new Error("Regex pattern contains nested quantifiers that risk catastrophic backtracking (ReDoS)");
|
|
21
41
|
}
|
|
22
42
|
return new RegExp(pattern, "i"); // throws SyntaxError on invalid patterns
|
|
23
43
|
}
|
|
24
44
|
const MAX_PREVIEW_LEN = 240;
|
|
45
|
+
const SECRET_REDACT_RE = /\b(?:AKIA[A-Z0-9]{16}|sk-[A-Za-z0-9]{32,}|ghp_[A-Za-z0-9]{36,}|xox[baprs]-[A-Za-z0-9-]{10,}|eyJ[A-Za-z0-9_-]{20,}(?:\.[A-Za-z0-9_-]{20,}){2})\b/g;
|
|
46
|
+
function redactSecrets(s) {
|
|
47
|
+
return s.replace(SECRET_REDACT_RE, "[REDACTED]");
|
|
48
|
+
}
|
|
25
49
|
function isHit(line, query, re) {
|
|
26
50
|
return re ? re.test(line) : line.includes(query);
|
|
27
51
|
}
|
|
@@ -35,13 +59,14 @@ function scanLines(file, lines, opts, re, matches) {
|
|
|
35
59
|
matches.push({
|
|
36
60
|
file,
|
|
37
61
|
line: i + 1,
|
|
38
|
-
preview: line.slice(0, MAX_PREVIEW_LEN)
|
|
62
|
+
preview: redactSecrets(line.slice(0, MAX_PREVIEW_LEN))
|
|
39
63
|
});
|
|
40
64
|
}
|
|
41
65
|
}
|
|
42
66
|
export async function searchRepo(opts) {
|
|
43
67
|
const files = await fg(["**/*.*"], {
|
|
44
68
|
dot: true,
|
|
69
|
+
followSymbolicLinks: false, // Prevent glob-based symlink traversal outside workspace root.
|
|
45
70
|
ignore: [
|
|
46
71
|
"**/node_modules/**",
|
|
47
72
|
"**/.git/**",
|
package/dist/review/store.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash, createHmac, randomUUID } from "node:crypto";
|
|
1
|
+
import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
const REVIEW_DIR = path.join(".mcp", "reviews");
|
|
@@ -40,6 +40,7 @@ const SAFE_SURFACE_RE = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
|
40
40
|
* Initialize a checklist for a run from the surface template.
|
|
41
41
|
*/
|
|
42
42
|
export async function initChecklist(runId, surface) {
|
|
43
|
+
assertRunId(runId); // CWE-22: validate UUID format before using as filename component
|
|
43
44
|
if (!SAFE_SURFACE_RE.test(surface)) {
|
|
44
45
|
throw new Error(`Invalid surface name "${surface}"`);
|
|
45
46
|
}
|
|
@@ -74,6 +75,7 @@ export async function initChecklist(runId, surface) {
|
|
|
74
75
|
* Mark a checklist item as completed.
|
|
75
76
|
*/
|
|
76
77
|
export async function completeChecklistItem(runId, itemId, completedBy, evidence) {
|
|
78
|
+
assertRunId(runId); // CWE-22
|
|
77
79
|
const state = await readChecklistRaw(runId);
|
|
78
80
|
if (!state)
|
|
79
81
|
throw new Error(`No checklist found for runId: ${runId}`);
|
|
@@ -93,6 +95,7 @@ export async function completeChecklistItem(runId, itemId, completedBy, evidence
|
|
|
93
95
|
* Mark a checklist item as not applicable.
|
|
94
96
|
*/
|
|
95
97
|
export async function markChecklistItemNA(runId, itemId, completedBy, reason) {
|
|
98
|
+
assertRunId(runId); // CWE-22
|
|
96
99
|
const state = await readChecklistRaw(runId);
|
|
97
100
|
if (!state)
|
|
98
101
|
throw new Error(`No checklist found for runId: ${runId}`);
|
|
@@ -111,6 +114,7 @@ export async function markChecklistItemNA(runId, itemId, completedBy, reason) {
|
|
|
111
114
|
* Mark a checklist item as failed.
|
|
112
115
|
*/
|
|
113
116
|
export async function failChecklistItem(runId, itemId, completedBy, reason) {
|
|
117
|
+
assertRunId(runId); // CWE-22
|
|
114
118
|
const state = await readChecklistRaw(runId);
|
|
115
119
|
if (!state)
|
|
116
120
|
throw new Error(`No checklist found for runId: ${runId}`);
|
|
@@ -129,6 +133,7 @@ export async function failChecklistItem(runId, itemId, completedBy, reason) {
|
|
|
129
133
|
* Sign off on a checklist. Requires all non-NA critical items to be completed.
|
|
130
134
|
*/
|
|
131
135
|
export async function signOffChecklist(runId, signedOffBy) {
|
|
136
|
+
assertRunId(runId); // CWE-22
|
|
132
137
|
const state = await readChecklistRaw(runId);
|
|
133
138
|
if (!state)
|
|
134
139
|
throw new Error(`No checklist found for runId: ${runId}`);
|
|
@@ -147,6 +152,7 @@ export async function signOffChecklist(runId, signedOffBy) {
|
|
|
147
152
|
* Read checklist state for a run.
|
|
148
153
|
*/
|
|
149
154
|
export async function readChecklist(runId) {
|
|
155
|
+
assertRunId(runId); // CWE-22
|
|
150
156
|
return readChecklistRaw(runId);
|
|
151
157
|
}
|
|
152
158
|
export async function createReviewRun(opts) {
|
|
@@ -202,7 +208,16 @@ export async function updateReviewStep(runId, step, status, details) {
|
|
|
202
208
|
await writeJson(reviewPath(run.id), run);
|
|
203
209
|
return run;
|
|
204
210
|
}
|
|
211
|
+
// HMAC-SHA256 requires a key of at least 32 bytes (256 bits) to provide full
|
|
212
|
+
// security. Keys shorter than the hash output degrade HMAC to effectively a
|
|
213
|
+
// keyed hash with reduced security margin (NIST SP 800-107 §5.3.4).
|
|
214
|
+
const HMAC_MIN_KEY_BYTES = 32;
|
|
205
215
|
export async function createReviewAttestation(runId, payload, signatureKey) {
|
|
216
|
+
if (signatureKey !== undefined && Buffer.byteLength(signatureKey, "utf-8") < HMAC_MIN_KEY_BYTES) {
|
|
217
|
+
throw new Error(`HMAC signature key is too short (${Buffer.byteLength(signatureKey, "utf-8")} bytes). ` +
|
|
218
|
+
`Provide a key of at least ${HMAC_MIN_KEY_BYTES} bytes (256 bits) — ` +
|
|
219
|
+
`generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`);
|
|
220
|
+
}
|
|
206
221
|
const digestInput = JSON.stringify(payload);
|
|
207
222
|
const sha256 = createHash("sha256").update(digestInput).digest("hex");
|
|
208
223
|
const hmacSha256 = signatureKey
|
|
@@ -221,3 +236,39 @@ export async function createReviewAttestation(runId, payload, signatureKey) {
|
|
|
221
236
|
hmacSha256
|
|
222
237
|
};
|
|
223
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Verify a stored attestation HMAC using a timing-safe comparison.
|
|
241
|
+
* Returns true only if the stored hmacSha256 matches the recomputed value.
|
|
242
|
+
* Uses timingSafeEqual to prevent timing oracle attacks on the comparison.
|
|
243
|
+
*/
|
|
244
|
+
export async function verifyAttestationHmac(runId, signatureKey) {
|
|
245
|
+
if (Buffer.byteLength(signatureKey, "utf-8") < HMAC_MIN_KEY_BYTES) {
|
|
246
|
+
return { valid: false, reason: "Signature key too short — cannot verify." };
|
|
247
|
+
}
|
|
248
|
+
let stored;
|
|
249
|
+
try {
|
|
250
|
+
const raw = await readFile(reportPath(runId), "utf-8");
|
|
251
|
+
stored = JSON.parse(raw);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return { valid: false, reason: "Attestation file not found or unreadable." };
|
|
255
|
+
}
|
|
256
|
+
const integrity = stored["integrity"];
|
|
257
|
+
const storedHmac = typeof integrity?.["hmacSha256"] === "string" ? integrity["hmacSha256"] : null;
|
|
258
|
+
if (!storedHmac) {
|
|
259
|
+
return { valid: false, reason: "Attestation was not signed — no hmacSha256 field." };
|
|
260
|
+
}
|
|
261
|
+
// Recompute HMAC over payload (everything except the integrity wrapper)
|
|
262
|
+
const { integrity: _stripped, ...payloadOnly } = stored;
|
|
263
|
+
const digestInput = JSON.stringify(payloadOnly);
|
|
264
|
+
const expected = createHmac("sha256", signatureKey).update(digestInput).digest("hex");
|
|
265
|
+
// Timing-safe comparison — prevents oracle attacks that leak the correct HMAC
|
|
266
|
+
// byte-by-byte via response timing differences (CWE-208).
|
|
267
|
+
const storedBuf = Buffer.from(storedHmac, "hex");
|
|
268
|
+
const expectedBuf = Buffer.from(expected, "hex");
|
|
269
|
+
if (storedBuf.length !== expectedBuf.length) {
|
|
270
|
+
return { valid: false, reason: "HMAC length mismatch." };
|
|
271
|
+
}
|
|
272
|
+
const match = timingSafeEqual(storedBuf, expectedBuf);
|
|
273
|
+
return match ? { valid: true } : { valid: false, reason: "HMAC mismatch — attestation may have been tampered." };
|
|
274
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "security-mcp",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "AI security MCP server and enforcement gate for Claude Code, Cursor, GitHub Copilot, Codex, Replit, and any MCP-compatible editor. Applies OWASP, MITRE ATT&CK, NIST, Zero Trust, PCI DSS, SOC 2, and ISO 27001.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,24 +61,24 @@
|
|
|
61
61
|
"test": "npm run build && node dist/tests/run.js"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
64
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
65
65
|
"execa": "^9.5.2",
|
|
66
66
|
"fast-glob": "^3.3.3",
|
|
67
67
|
"picomatch": "^4.0.4",
|
|
68
68
|
"zod": "^3.24.1"
|
|
69
69
|
},
|
|
70
70
|
"overrides": {
|
|
71
|
-
"express-rate-limit": "
|
|
72
|
-
"hono": "
|
|
71
|
+
"express-rate-limit": "8.5.2",
|
|
72
|
+
"hono": "4.12.23"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@eslint/js": "^9.22.0",
|
|
76
|
-
"@types/node": "^
|
|
77
|
-
"@types/picomatch": "^4.0.
|
|
76
|
+
"@types/node": "^24.12.4",
|
|
77
|
+
"@types/picomatch": "^4.0.3",
|
|
78
78
|
"eslint": "^9.22.0",
|
|
79
79
|
"globals": "^16.0.0",
|
|
80
80
|
"typescript": "^5.7.3",
|
|
81
|
-
"typescript-eslint": "^8.
|
|
81
|
+
"typescript-eslint": "^8.60.0"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=20"
|
|
@@ -32,6 +32,79 @@ When you find a vulnerability, you do exactly this:
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
+
## §0 COVERAGE COMPLETENESS PROTOCOL — THIS RUNS BEFORE EVERYTHING ELSE
|
|
36
|
+
|
|
37
|
+
You cannot declare a codebase clean without proving you checked every file.
|
|
38
|
+
You cannot close a finding without proving your fix eliminates it.
|
|
39
|
+
|
|
40
|
+
### Step 1 — Complete File Inventory (NO EXCEPTIONS)
|
|
41
|
+
|
|
42
|
+
Before any analysis begins:
|
|
43
|
+
|
|
44
|
+
1. Run `repo.search` with `.*` pattern or use Glob to enumerate ALL source files.
|
|
45
|
+
2. Write the complete file list to `.mcp/agent-runs/{runId}/coverage-manifest.json`:
|
|
46
|
+
`{ "totalFiles": N, "files": [{"path": "...", "status": "pending"}] }`
|
|
47
|
+
3. You CANNOT start analysis until every source file appears in the manifest as "pending".
|
|
48
|
+
4. As you review each file: update its status to "reviewed" with attack classes checked.
|
|
49
|
+
5. You CANNOT call any completion step until every file is "reviewed".
|
|
50
|
+
|
|
51
|
+
### Step 2 — Taint Tracking (MANDATORY FOR EVERY USER-CONTROLLED INPUT)
|
|
52
|
+
|
|
53
|
+
For every source of untrusted data found in the codebase, trace ALL downstream paths:
|
|
54
|
+
|
|
55
|
+
- `req.body`, `req.query`, `req.params`, `req.headers` (HTTP)
|
|
56
|
+
- `event.data`, `socket.message` (WebSocket/event-driven)
|
|
57
|
+
- `process.env[variable]` passed through to functions (env injection)
|
|
58
|
+
- Database results that originated from user input
|
|
59
|
+
- External API responses used in downstream processing
|
|
60
|
+
- File contents read from user-uploaded files
|
|
61
|
+
- URL fragments / hash passed via JavaScript
|
|
62
|
+
|
|
63
|
+
Classify each sink as:
|
|
64
|
+
|
|
65
|
+
- **SAFE**: parameterized query, schema-validated output, sanitized before use
|
|
66
|
+
- **UNSAFE**: raw SQL concat, eval, shell exec, unvalidated redirect, unencoded output
|
|
67
|
+
- **UNRESOLVED**: tracing blocked (e.g., function in a third-party module)
|
|
68
|
+
|
|
69
|
+
Any UNRESOLVED path is treated as UNSAFE until proven otherwise.
|
|
70
|
+
Write the taint map to `.mcp/agent-runs/{runId}/taint-map.json`.
|
|
71
|
+
|
|
72
|
+
### Step 3 — Negative Assertion Protocol (MANDATORY PER ATTACK CLASS)
|
|
73
|
+
|
|
74
|
+
After reviewing each attack class, you MUST write one of these statements:
|
|
75
|
+
|
|
76
|
+
**FORMAT:** `ATTACK CLASS: {name} | FILES CHECKED: {n}/{total} | PATTERNS SEARCHED: {list} | RESULT: {CLEAN | N findings (N/N fixed)}`
|
|
77
|
+
|
|
78
|
+
**Example (clean):** `ATTACK CLASS: SQL Injection | FILES CHECKED: 47/47 | PATTERNS SEARCHED: queryRaw, executeRaw, string concat in query context | RESULT: CLEAN`
|
|
79
|
+
|
|
80
|
+
**Example (fixed):** `ATTACK CLASS: JWT Algorithm Confusion | FILES CHECKED: 47/47 | PATTERNS SEARCHED: jwt.verify without algorithms pin | RESULT: 2 findings (2/2 fixed)`
|
|
81
|
+
|
|
82
|
+
You CANNOT report a class as CLEAN without examining every file in the manifest.
|
|
83
|
+
|
|
84
|
+
### Step 4 — Fix Verification Loop (MANDATORY FOR EVERY FINDING)
|
|
85
|
+
|
|
86
|
+
After writing a fix:
|
|
87
|
+
|
|
88
|
+
1. Re-run the SAME pattern search or gate check that triggered the finding.
|
|
89
|
+
2. Confirm the finding no longer fires.
|
|
90
|
+
3. If the finding still fires: fix again. Do NOT proceed to the next finding.
|
|
91
|
+
4. Write to the coverage manifest: `{ "finding": "ID", "fixedAt": "path:line", "verifiedClean": true }`
|
|
92
|
+
|
|
93
|
+
You CANNOT mark a finding "resolved" without a verified-clean gate re-run.
|
|
94
|
+
|
|
95
|
+
### Step 5 — All-or-Nothing Fix Mandate
|
|
96
|
+
|
|
97
|
+
If a finding CANNOT be fixed in this session (architectural change required):
|
|
98
|
+
|
|
99
|
+
- The gate check for that attack class MUST remain failing (do NOT suppress).
|
|
100
|
+
- Write a detailed remediation plan to `.mcp/agent-runs/{runId}/deferred-fixes.json`.
|
|
101
|
+
- Create a risk-acceptance record: `{ "finding": "ID", "owner": "...", "ticket": "...", "dueDate": "...", "compensatingControl": "..." }`
|
|
102
|
+
- The merge gate MUST block on this finding until the ticket is closed.
|
|
103
|
+
|
|
104
|
+
**No finding is ever "noted and moved on." It is either FIXED or BLOCKED.**
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
35
108
|
## ROLE
|
|
36
109
|
|
|
37
110
|
You are a **Senior Security Engineer**. Your operating ratio is **90% fixing, 10% advisory**.
|
|
@@ -97,3 +97,102 @@ Every finding must include:
|
|
|
97
97
|
- `requiredActions`: ordered list of actions if not auto-remediated
|
|
98
98
|
- `complianceImpact`: framework mappings
|
|
99
99
|
- `beyondSkillMd`: true if this finding goes beyond the SKILL.md mandate
|
|
100
|
+
|
|
101
|
+
## BEYOND SKILL.MD — MANDATORY EXPANSIONS
|
|
102
|
+
|
|
103
|
+
List 6-8 specific edge cases and emerging threats beyond the standard SKILL.md mandate. Pattern:
|
|
104
|
+
- **[Topic]:** [Specific scenario with concrete test or detection method]
|
|
105
|
+
- Each expansion must name a specific CVE, framework, attack technique, or research paper
|
|
106
|
+
- Must include at least 2 post-quantum or AI-era threats
|
|
107
|
+
|
|
108
|
+
- **Prototype Pollution via Merge Utilities (CVE-2019-10744 class):** Attacker-controlled JSON reaching `_.merge`, `Object.assign`, or `structuredClone` can poison `Object.prototype`. Test: send `{"__proto__":{"isAdmin":true}}` to every JSON ingestion endpoint; verify `{}.isAdmin` remains `undefined` after processing.
|
|
109
|
+
- **HTTP Request Smuggling (CL.TE / TE.CL — CVE-2019-18277 class):** Reverse proxies and origin servers disagree on body length, allowing prefix injection into the next victim's request. Test with Burp HTTP Request Smuggler; look for mismatched `Transfer-Encoding` and `Content-Length` handling across load-balancer and app-server pairs.
|
|
110
|
+
- **Server-Side Template Injection via User-Supplied Filenames (T1059.007):** Template engines (Jinja2, Pebble, Handlebars) resolve partials from user input. Inject `{{7*7}}` or `${7*7}` in filename fields; a `49` in the response confirms SSTI without alerting WAFs tuned for URL parameters.
|
|
111
|
+
- **SAML Signature Wrapping (XSW — research: "Bursting the Bubble" 2012, still unpatched in many IdPs):** Duplicate the signed `Assertion` node; place a malicious unsigned assertion where the SP validates. Test by cloning the signed element, modifying `NameID`, and inserting both into the `Response` doc. Libraries using XPath position (not ID) are vulnerable.
|
|
112
|
+
- **Post-Quantum Harvest-Now-Decrypt-Later (NIST IR 8413):** Adversaries archive TLS sessions today to decrypt once a cryptographically relevant quantum computer (CRQC) exists. Any RSA-2048/ECDH key exchange protects data only until ~2030. Detect by inventorying all TLS handshakes that do not negotiate a hybrid ML-KEM (X25519Kyber768) key exchange using `openssl s_client` captures.
|
|
113
|
+
- **LLM-Powered Automated Exploit Generation (AI-era threat — "LLM Agents for Offensive Security", arXiv 2405.02929):** Attackers use fine-tuned LLMs to generate working PoC exploits from CVE descriptions in under 60 seconds. This means the window between patch release and weaponised exploit is collapsing toward hours. Detect exposure: check `npm audit` / `trivy` outputs for any CVE older than 48 hours that lacks a patch applied to the running container image.
|
|
114
|
+
- **Subdomain Takeover via Dangling CNAME (T1584.001):** DNS CNAME records pointing to deprovisioned cloud resources (S3, Heroku, Azure Static Web Apps) can be claimed by an attacker. Enumerate all CNAME records in DNS; resolve each; flag any that return NXDOMAIN or provider-specific "not found" pages. Automate with `subjack` or `nuclei -t takeovers/`.
|
|
115
|
+
- **OAuth 2.0 Authorization Code Injection via State Parameter Fixation (CVE-2022-24442 class):** If `state` is not bound to the user session before the redirect, an attacker can inject a valid `code` from their own flow into the victim's session. Test: complete an OAuth flow as attacker, capture the `code`, reset the victim session, replay the `code` in the victim's callback URL — authentication should fail if `state` is properly validated.
|
|
116
|
+
|
|
117
|
+
## §EDGE-CASE-MATRIX
|
|
118
|
+
|
|
119
|
+
The 5 attack cases in this domain that automated scanners and naive manual review universally miss. MANDATORY checks — do not skip.
|
|
120
|
+
|
|
121
|
+
| # | Edge Case | Why Scanners Miss It | Concrete Test |
|
|
122
|
+
|---|-----------|----------------------|---------------|
|
|
123
|
+
| 1 | Second-order / stored payload executed in different context | Scanner checks input context, not execution context | Store payload safely; trigger in separate request/session |
|
|
124
|
+
| 2 | Unicode normalisation bypass | Regex filters run before normalisation; attacker uses homoglyphs or composed forms | Submit Ⅰ (U+2160) or < (U+FF1C) variants of known-bad strings |
|
|
125
|
+
| 3 | Polyglot payload active in multiple sinks simultaneously | Scanners test one injection class per payload | `'"><script>{{7*7}}</script><!--` — SQL + XSS + SSTI in one request |
|
|
126
|
+
| 4 | Out-of-band exfiltration (DNS/HTTP callback) | Scanner looks for inline response difference; OOB leaves no visible trace | Use Burp Collaborator / interactsh; inject DNS lookup payload |
|
|
127
|
+
| 5 | Race condition between check and use (TOCTOU) | Sequential scanners don't model concurrency | Send two simultaneous requests to the same state-changing endpoint |
|
|
128
|
+
|
|
129
|
+
## §TEMPORAL-THREATS
|
|
130
|
+
|
|
131
|
+
Threats materialising in the 2025–2030 window that defences designed today must account for.
|
|
132
|
+
|
|
133
|
+
| Threat | Est. Timeline | Relevance to This Domain | Prepare Now By |
|
|
134
|
+
|--------|--------------|--------------------------|----------------|
|
|
135
|
+
| Cryptographically Relevant Quantum Computer (CRQC) | 2028–2032 | Harvest-now-decrypt-later attacks active today; RSA/ECDSA keys signed today will be broken | Inventory all RSA/ECDSA usage; migrate long-lived data to ML-KEM (FIPS 203) |
|
|
136
|
+
| AI-assisted adversaries at scale | 2025–2027 (active) | LLM-powered fuzzing finds 10× more edge cases; automated PoC generation | Assume attackers have LLM help; expand test surface to match |
|
|
137
|
+
| EU AI Act full enforcement | 2026 | High-risk AI systems require mandatory conformity assessments | Classify all AI features against AI Act tiers now |
|
|
138
|
+
| Post-quantum TLS migration deadline | 2028–2030 | Browser vendors will drop classical-only TLS connections | Begin TLS agility assessment; test hybrid key exchange |
|
|
139
|
+
| Mandatory SBOM + build provenance (US EO 14028 / EU CRA) | 2025–2026 (active) | SBOM and SLSA attestation are becoming legally required | Achieve SLSA L2 minimum; generate CycloneDX SBOM per release |
|
|
140
|
+
|
|
141
|
+
## §DETECTION-GAP
|
|
142
|
+
|
|
143
|
+
What current security monitoring CANNOT detect in this domain, and what to build to close each gap.
|
|
144
|
+
|
|
145
|
+
**Standard gaps that MUST be checked:**
|
|
146
|
+
|
|
147
|
+
- **Second-order attack execution**: The storage request looks safe; only the retrieval+execution step is dangerous. Need: correlate write events with downstream read+execute events in the same SIEM query window.
|
|
148
|
+
- **Timing-side-channel leakage**: No log event emitted; only observable as microsecond response-time variance. Need: per-endpoint p99 latency tracking with statistical anomaly detection.
|
|
149
|
+
- **Low-and-slow credential stuffing**: Individually, each request is under rate limits. Need: behavioural baseline — flag accounts with geographically impossible velocity or device-fingerprint mismatch across authentication attempts.
|
|
150
|
+
- **Insider exfiltration via legitimate process**: Authorised exports, reports, and data downloads that individually are permitted but collectively constitute data exfiltration. Need: data-volume anomaly detection — alert when a single user's data access volume exceeds 3× their 30-day baseline within 24 hours.
|
|
151
|
+
- **Cross-agent attack chains**: Phase 1 finding A + Phase 1 finding B = CRITICAL chain invisible to either agent alone. Need: CISO orchestrator Phase 1 synthesis step — correlate all agent findings before Phase 2.
|
|
152
|
+
|
|
153
|
+
## §ZERO-MISS-MANDATE
|
|
154
|
+
|
|
155
|
+
This agent CANNOT declare any attack class clean without explicit evidence of checking. For each item, output one of:
|
|
156
|
+
- `CHECKED: [N files] | [patterns used] | CLEAN`
|
|
157
|
+
- `CHECKED: [N files] | [patterns used] | [N findings, all fixed]`
|
|
158
|
+
- `SKIPPED: [reason — must be "not applicable: [evidence]"]`
|
|
159
|
+
|
|
160
|
+
**Silent skip = FAILED COVERAGE.** The orchestrator flags this as a quality gap.
|
|
161
|
+
|
|
162
|
+
The output findings JSON MUST include a `coverageManifest` key:
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"coverageManifest": {
|
|
166
|
+
"attackClassesCovered": [{ "class": "SQL Injection", "filesReviewed": 47, "patterns": ["queryRaw", "string concat"], "result": "CLEAN" }],
|
|
167
|
+
"filesReviewed": 47,
|
|
168
|
+
"negativeAssertions": ["SQL Injection: queryRaw pattern searched across 47 files — 0 matches"],
|
|
169
|
+
"uncoveredReason": {}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## LEARNING SIGNAL
|
|
175
|
+
|
|
176
|
+
On every finding resolved, emit:
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"findingId": "FINDING_ID",
|
|
180
|
+
"agentName": "AGENT_NAME",
|
|
181
|
+
"resolved": true,
|
|
182
|
+
"remediationTemplate": "one-line description of what was done",
|
|
183
|
+
"falsePositive": false
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
Call `security.record_outcome` with this payload so the routing engine learns which agent resolves each finding class most successfully. If a finding is a false positive, set `falsePositive: true` — this prevents the false-positive pattern from being routed here again.
|
|
187
|
+
|
|
188
|
+
Every findings JSON MUST include `intelligenceForOtherAgents`:
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"intelligenceForOtherAgents": {
|
|
192
|
+
"forPentestTeam": [{ "type": "HIGH_VALUE_TARGET", "description": "...", "exploitHint": "..." }],
|
|
193
|
+
"forCryptoSpecialist": [{ "type": "CRYPTO_WEAKNESS_REFERENCE", "algorithm": "...", "location": "..." }],
|
|
194
|
+
"forCloudSpecialist": [{ "type": "SSRF_TO_CLOUD_CHAIN", "ssrfLocation": "...", "escalationPath": "..." }],
|
|
195
|
+
"forComplianceGrc": [{ "type": "COMPLIANCE_BLOCKER", "frameworks": ["..."], "releaseBlock": true }]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
@@ -223,3 +223,112 @@ export async function getOrCompute<T>(key: string, compute: () => Promise<T>): P
|
|
|
223
223
|
- `requiredActions`: ordered action list
|
|
224
224
|
- `complianceImpact`: framework mappings
|
|
225
225
|
- `beyondSkillMd`: true if finding goes beyond the SKILL.md mandate
|
|
226
|
+
- `intelligenceForOtherAgents`: structured hints for downstream specialist agents (schema below)
|
|
227
|
+
|
|
228
|
+
Every findings JSON MUST include `intelligenceForOtherAgents`:
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"intelligenceForOtherAgents": {
|
|
232
|
+
"forPentestTeam": [{ "type": "HIGH_VALUE_TARGET", "description": "Endpoint with no connection limit is trivially Slowlorisable", "exploitHint": "Open 1000 connections sending 1 byte/10s; monitor for 503s" }],
|
|
233
|
+
"forCloudSpecialist": [{ "type": "COST_AMPLIFICATION_CHAIN", "lambdaLocation": "src/handlers/process.ts", "escalationPath": "Unauthenticated POST triggers cold-start flood → unbounded concurrency → $k/min bill" }],
|
|
234
|
+
"forComplianceGrc": [{ "type": "COMPLIANCE_BLOCKER", "frameworks": ["SOC2-A1.1", "PCI-DSS-Req6.4.1"], "releaseBlock": true }],
|
|
235
|
+
"forNetworkSpecialist": [{ "type": "AMPLIFICATION_VECTOR", "protocol": "QUIC/UDP", "description": "Server reflects 30× amplified responses to spoofed source IPs" }]
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## BEYOND SKILL.MD — MANDATORY EXPANSIONS
|
|
243
|
+
|
|
244
|
+
- **HTTP/2 Rapid Reset Mass Exploitation (CVE-2023-44487 / ATT&CK T1499.003):** The Cloudflare/AWS/Google coordinated disclosure confirmed single-client 390M rps via pipelined HEADERS+RST_STREAM frames, bypassing all traditional volumetric thresholds. Test by: run `h2load -n 5000000 -c 100 -m 1000 --rps=500000 <target>` and monitor nginx `reset_timedout_connection` counters; if `RST_STREAM` rate exceeds 1000/s per IP without session teardown, the server is vulnerable. Finding threshold: any HTTP/2 server lacking `http2_max_concurrent_streams ≤ 128` AND per-session RST rate enforcement is CRITICAL.
|
|
245
|
+
|
|
246
|
+
- **AI-Assisted Adaptive Traffic Shape Evasion (ATT&CK T1499.002):** Threat actors (documented in Cloudflare 2024 DDoS Threat Report Q3) now deploy LLM-generated request sequences that mutate User-Agent, header order, TLS fingerprint (JA3), and inter-arrival timing every 30 seconds to evade ML-based rate limiters trained on static historical baselines. Test by: record a 5-minute baseline of normal traffic, then replay with `mitmproxy` + a GPT-4o script that randomises JA3/ALPN per request while maintaining target RPS; verify the WAF blocks it. Finding threshold: if the WAF's block rate drops below 80% within 2 minutes of mutation, the detection is insufficient.
|
|
247
|
+
|
|
248
|
+
- **QUIC/UDP Amplification via Initial Packet Reflection (IETF RFC 9000 §8 / CVE-2024-45322):** QUIC servers that do not enforce address validation tokens reflect server Initial packets (~1200 bytes) in response to spoofed client Initials (~300 bytes), yielding a 4× amplification factor. Cloudflare disclosed active exploitation of misconfigured QUIC endpoints in 2024. Test by: use `quic-go`'s `quic-client` tool with a spoofed source IP on a controlled test network; measure outbound bytes vs. inbound bytes at the server; a ratio > 2× without token enforcement is a HIGH finding. Finding threshold: any QUIC listener without `quic.Config{RequireAddressValidation: func(net.Addr) bool { return true }}` or equivalent nginx `quic_gso on; quic_retry on` is flagged.
|
|
249
|
+
|
|
250
|
+
- **Serverless Cold-Start Cost Amplification via Unauthenticated Fan-Out (ATT&CK T1496 — Resource Hijacking):** Breaches at Codecov (2021) and the Twilio supply chain incident demonstrated that a single unauthenticated POST to an event ingestion webhook can fan out to dozens of Lambda handlers simultaneously. With no reserved concurrency, $1 of attacker egress can generate $2 000+ in Lambda invocation costs within 60 seconds (documented in Datadog's 2024 State of Serverless report). Test by: identify all unauthenticated or weakly authenticated event endpoints (`POST /webhook`, `/events`, `/ingest`); send 500 concurrent requests and observe CloudWatch `ConcurrentExecutions` across downstream handlers. Finding threshold: any unauthenticated endpoint triggering > 3 downstream Lambda invocations per request, without reserved concurrency caps on all handlers, is CRITICAL.
|
|
251
|
+
|
|
252
|
+
- **Post-Quantum TLS Handshake Size DoS (NIST FIPS 203/204 — Kyber/Dilithium transition):** Kyber-1024 public keys are 1568 bytes vs. 65 bytes for P-256; Dilithium3 signatures are 3293 bytes. A TLS 1.3 handshake with post-quantum hybrid key exchange (X25519Kyber768) inflates ClientHello to ~2 KB, requiring TCP fragmentation. Servers processing thousands of incomplete PQ handshakes simultaneously face 10–20× memory amplification compared to classical TLS — a vector Cloudflare Research documented in their PQ migration analysis (2024). Test by: configure `openssl s_client -curves X25519MLKEM768` against the target and flood with 10 000 concurrent half-open TLS handshakes (send ClientHello, then stall); monitor server TLS session table memory. Finding threshold: if server memory grows > 500 MB from 10 000 half-open PQ handshakes without a handshake timeout of ≤ 10 s, flag as HIGH.
|
|
253
|
+
|
|
254
|
+
- **Supply Chain DoS via Malicious npm Dependency Introducing Unbounded Recursion (ATT&CK T1195.001):** The `event-stream` (2018) and `node-ipc` (2022) supply chain incidents demonstrated that widely-used packages can inject deliberate resource exhaustion. A dependency that introduces unbounded synchronous recursion or a `while(true)` on a hot path can cause 100% CPU saturation without any external traffic. Test by: run `npm audit --json | jq '[.vulnerabilities[] | select(.severity == "high" or .severity == "critical")]'` and cross-reference each HIGH/CRITICAL dependency against OSV.dev for DoS-class CVEs; additionally run `node --prof` during a load test and inspect the flamegraph for unexpectedly deep call stacks (> 500 frames) in third-party modules. Finding threshold: any production dependency with an open DoS-class CVE (CWE-400, CWE-674, CWE-835) that has a patched version available is CRITICAL; unpatched with no available fix is HIGH with mandatory vendor notification.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## §EDGE-CASE-MATRIX
|
|
259
|
+
|
|
260
|
+
The 5 DoS attack cases that automated scanners and naive manual review universally miss.
|
|
261
|
+
|
|
262
|
+
| # | Edge Case | Why Scanners Miss It | Concrete Test |
|
|
263
|
+
|---|-----------|----------------------|---------------|
|
|
264
|
+
| 1 | HTTP/2 Rapid Reset with RST_STREAM pipelining | Scanners send sequential requests; rapid reset requires simultaneous HEADERS+RST_STREAM at volume | Use `h2load -n 1000000 -c 10 -m 200 --rps=100000 --header=":method: POST"` then monitor RST_STREAM counters; server should kill the session before 390M rps is reached |
|
|
265
|
+
| 2 | Application-layer amplification via authenticated cache endpoint | Auth gates are assumed to prevent DoS; once past auth, a single request may populate cache with a 10 MB object served to 100k concurrent readers | Log in once, hit `GET /api/report/generate` with a large `?range=` parameter; measure egress multiplier vs. one request's compute cost |
|
|
266
|
+
| 3 | Slow POST / R.U.D.Y. body trickle bypassing header timeouts | `client_header_timeout` fires on missing headers, NOT on a valid header with body sent at 1 byte/10s | Open connection, send valid headers + `Content-Length: 100000`, then drip body at 1 byte per 15 seconds; count threads consumed in 60 seconds |
|
|
267
|
+
| 4 | WebSocket per-frame fragmentation flood | Rate limiters count messages, not frames; WS spec allows a single message split into thousands of frames | Send a single logical message as 50 000 continuation frames with `FIN=0`; verify server enforces a max-frames-per-message or max-message-size limit |
|
|
268
|
+
| 5 | Cloud Function cold-start storm via unauthenticated event fan-out | Concurrency limits protect individual functions; fan-out triggers N distinct functions simultaneously | POST to an event ingestion endpoint that fans out to 20 Lambda downstream handlers, each without reserved concurrency; verify total concurrent invocations stay within budget |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## §TEMPORAL-THREATS
|
|
273
|
+
|
|
274
|
+
Threats materialising in the 2025–2030 window that DoS defences designed today must account for.
|
|
275
|
+
|
|
276
|
+
| Threat | Est. Timeline | Relevance to DoS Domain | Prepare Now By |
|
|
277
|
+
|--------|--------------|--------------------------|----------------|
|
|
278
|
+
| AI-assisted L7 DoS (LLM-generated adaptive traffic shapes) | 2025–2027 (active) | Attackers use LLMs to generate request patterns that evade rate-limit signatures tuned on historical traffic | Move from signature-based rate limits to behavioural anomaly baselines; per-endpoint p99 latency is a leading indicator |
|
|
279
|
+
| HTTP/3 + QUIC amplification at scale | 2025–2026 | QUIC's UDP base enables spoofed-source amplification; server initial packets can be 3–8× larger than client hellos | Enable QUIC address validation tokens; set `max_udp_payload_size` conservatively; test with `quic-go` amplification tooling |
|
|
280
|
+
| Serverless / edge cold-start as a cost weapon | 2025 (active) | Attacker spends $1 on egress; victim pays $500–$5 000 in Lambda/Cloudflare Worker cold-starts and invocations | Enforce reserved concurrency on every Lambda; set Cloudflare Workers CPU limits; configure spend alerts at 50%/80%/100% of monthly budget |
|
|
281
|
+
| gRPC server streaming without deadline propagation | 2025–2026 | As gRPC adoption rises, deadline-less streams let attackers hold server goroutines/threads indefinitely | Audit every `grpc.ServerStream` handler for `ctx.Deadline()` enforcement; add integration test that cancels client after 5 s and asserts server stream terminates within 1 s |
|
|
282
|
+
| Mandatory cloud spend controls (FinOps / CSP policy enforcement) | 2026 | Cloud providers will enforce organisation-level spend caps that can DoS the victim's own service if triggered by an attacker | Architect spend caps with auto-scaling floors to prevent self-inflicted outage; use AWS Cost Anomaly Detection + SNS, not hard cutoffs |
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## §DETECTION-GAP
|
|
287
|
+
|
|
288
|
+
What current monitoring CANNOT detect in the DoS domain, and what to build to close each gap.
|
|
289
|
+
|
|
290
|
+
- **Slow-body / R.U.D.Y. attacks**: Standard connection count metrics are flat — attacker holds one connection per thread, which is "normal." Need: per-connection bytes-received-per-second histogram; alert when p50 drops below 100 bytes/s across more than 5% of active connections.
|
|
291
|
+
- **HTTP/2 RST_STREAM abuse before session teardown**: Request-per-second dashboards never see the requests — they are opened and immediately reset. Need: instrument the `session.on("stream")` and `stream.on("close")` events separately; alert when `RST_without_response_rate > 20%` per IP.
|
|
292
|
+
- **Cache stampede cascade**: Individual cache-miss latency looks like a normal spike. The signal is N identical cache misses at the exact same millisecond after a TTL expiry. Need: correlate cache-miss events by key in a 100 ms window; alert when the same key misses > 10 times simultaneously.
|
|
293
|
+
- **Lambda cold-start cost amplification**: CloudWatch shows invocation count but not cost velocity. By the time the monthly budget alert fires, the damage is done. Need: real-time spend rate alarm (`EstimatedCharges` metric, 1-minute period, alert at 2× daily average) with an SNS-to-Lambda circuit breaker that drops reserved concurrency to 0 for compromised functions.
|
|
294
|
+
- **Cross-protocol amplification (UDP reflection)**: TCP-based IDS/WAF is blind to UDP amplification sourced through the application's QUIC or DNS endpoints. Need: netflow analysis at the edge with a source-IP fan-out ratio alert (flag any IP receiving > 10× the bytes it sent in a 30-second window).
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## §ZERO-MISS-MANDATE
|
|
299
|
+
|
|
300
|
+
This agent CANNOT declare any DoS attack class clean without explicit evidence of checking. For each item, output one of:
|
|
301
|
+
- `CHECKED: [N files] | [patterns used] | CLEAN`
|
|
302
|
+
- `CHECKED: [N files] | [patterns used] | [N findings, all fixed]`
|
|
303
|
+
- `SKIPPED: [reason — must be "not applicable: [evidence]"]`
|
|
304
|
+
|
|
305
|
+
**Silent skip = FAILED COVERAGE.** The orchestrator flags this as a quality gap.
|
|
306
|
+
|
|
307
|
+
**Required coverage classes for advanced-dos-tester:**
|
|
308
|
+
|
|
309
|
+
| Attack Class | Minimum Grep / Config Check |
|
|
310
|
+
|---|---|
|
|
311
|
+
| HTTP/2 Rapid Reset | `http2Settings`, `maxConcurrentStreams`, nginx `http2_max_concurrent_streams` |
|
|
312
|
+
| Slowloris / Slow Headers | `keepalive_timeout`, `client_header_timeout`, `headersTimeout` |
|
|
313
|
+
| Slow POST / R.U.D.Y. | `client_body_timeout`, `bodyTimeout`, `requestTimeout` |
|
|
314
|
+
| WebSocket flood | `ws`, `socket.io` + per-connection rate limit present |
|
|
315
|
+
| gRPC streaming DoS | `grpc.ServerStream`, `ctx.Deadline`, `grpc.MaxConcurrentStreams` |
|
|
316
|
+
| Cache stampede | `redis.get`/`set` near expensive compute + mutex/lock present |
|
|
317
|
+
| Lambda/Function cost amplification | `reserved_concurrent_executions`, `maxInstances`, budget alert resource |
|
|
318
|
+
| QUIC/UDP amplification | QUIC config with address validation token enabled |
|
|
319
|
+
| Cloud budget alerting | `aws_budgets_budget`, `google_billing_budget`, or equivalent IaC resource |
|
|
320
|
+
|
|
321
|
+
The output findings JSON MUST include a `coverageManifest` key:
|
|
322
|
+
```json
|
|
323
|
+
{
|
|
324
|
+
"coverageManifest": {
|
|
325
|
+
"attackClassesCovered": [
|
|
326
|
+
{ "class": "HTTP/2 Rapid Reset", "filesReviewed": 12, "patterns": ["maxConcurrentStreams", "http2_max_concurrent_streams"], "result": "CLEAN" },
|
|
327
|
+
{ "class": "Lambda Cost Amplification", "filesReviewed": 8, "patterns": ["reserved_concurrent_executions", "aws_budgets_budget"], "result": "2 findings, both fixed" }
|
|
328
|
+
],
|
|
329
|
+
"filesReviewed": 47,
|
|
330
|
+
"negativeAssertions": ["Slowloris: client_header_timeout present in all 3 Nginx configs — 0 gaps"],
|
|
331
|
+
"uncoveredReason": {}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|