security-mcp 1.1.0 → 1.1.2
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 +966 -193
- package/defaults/agent-run-schema.json +98 -0
- package/dist/ci/pr-gate.js +18 -1
- package/dist/cli/install.js +69 -2
- package/dist/cli/onboarding.js +82 -11
- package/dist/cli/update.js +83 -15
- package/dist/gate/checks/ai-redteam.js +83 -59
- package/dist/gate/checks/api.js +93 -0
- package/dist/gate/checks/ci-pipeline.js +135 -0
- package/dist/gate/checks/crypto.js +91 -22
- package/dist/gate/checks/database.js +5 -1
- package/dist/gate/checks/dependencies.js +297 -2
- package/dist/gate/checks/dlp.js +6 -1
- package/dist/gate/checks/graphql.js +6 -1
- package/dist/gate/checks/k8s.js +229 -181
- package/dist/gate/checks/nuclei.js +133 -0
- package/dist/gate/checks/runtime.js +75 -8
- package/dist/gate/checks/scanners.js +8 -2
- package/dist/gate/diff.js +2 -0
- package/dist/gate/exceptions.js +6 -1
- package/dist/gate/policy.js +47 -4
- package/dist/gate/result.js +7 -1
- package/dist/mcp/audit-chain.js +253 -0
- package/dist/mcp/learning.js +228 -0
- package/dist/mcp/model-router.js +544 -0
- package/dist/mcp/orchestration.js +604 -0
- package/dist/mcp/server.js +160 -12
- package/dist/repo/search.js +5 -7
- package/dist/review/store.js +15 -0
- package/dist/types/agent-run.js +8 -0
- package/package.json +5 -5
- package/skills/_TEMPLATE/SKILL.md +99 -0
- package/skills/advanced-dos-tester/SKILL.md +225 -0
- package/skills/agentic-loop-exploiter/SKILL.md +69 -0
- package/skills/ai-llm-redteam/SKILL.md +118 -0
- package/skills/ai-model-supply-chain-agent/SKILL.md +198 -0
- package/skills/algorithm-implementation-reviewer/SKILL.md +85 -0
- package/skills/android-penetration-tester/SKILL.md +83 -0
- package/skills/anti-replay-tester/SKILL.md +195 -0
- package/skills/appsec-code-auditor/SKILL.md +86 -0
- package/skills/artifact-integrity-analyst/SKILL.md +68 -0
- package/skills/attack-navigator/SKILL.md +64 -0
- package/skills/auth-session-hacker/SKILL.md +87 -0
- package/skills/aws-penetration-tester/SKILL.md +60 -0
- package/skills/azure-penetration-tester/SKILL.md +64 -0
- package/skills/binary-auth-validator/SKILL.md +184 -0
- package/skills/bot-detection-specialist/SKILL.md +221 -0
- package/skills/business-logic-attacker/SKILL.md +76 -0
- package/skills/capec-code-mapper/SKILL.md +163 -0
- package/skills/cert-pin-rotation-specialist/SKILL.md +200 -0
- package/skills/cicd-pipeline-hijacker/SKILL.md +81 -0
- package/skills/ciso-orchestrator/SKILL.md +165 -0
- package/skills/cloud-infra-specialist/SKILL.md +85 -0
- package/skills/compliance-gap-analyst/SKILL.md +77 -0
- package/skills/compliance-grc/SKILL.md +148 -0
- package/skills/compliance-lifecycle-tracker/SKILL.md +169 -0
- package/skills/credential-stuffing-specialist/SKILL.md +192 -0
- package/skills/crypto-pki-specialist/SKILL.md +136 -0
- package/skills/csa-ccm-mapper/SKILL.md +178 -0
- package/skills/csf2-governance-mapper/SKILL.md +159 -0
- package/skills/deep-link-fuzzer/SKILL.md +195 -0
- package/skills/dependency-confusion-attacker/SKILL.md +78 -0
- package/skills/device-integrity-aggregator/SKILL.md +221 -0
- package/skills/dos-resilience-tester/SKILL.md +184 -0
- package/skills/dread-scorer/SKILL.md +157 -0
- package/skills/egress-policy-enforcer/SKILL.md +208 -0
- package/skills/evidence-collector/SKILL.md +86 -0
- package/skills/file-upload-attacker/SKILL.md +208 -0
- package/skills/gcp-penetration-tester/SKILL.md +63 -0
- package/skills/git-history-secret-scanner/SKILL.md +182 -0
- package/skills/iam-privesc-graph-builder/SKILL.md +216 -0
- package/skills/incident-responder/SKILL.md +192 -0
- package/skills/injection-specialist/SKILL.md +62 -0
- package/skills/ios-security-auditor/SKILL.md +77 -0
- package/skills/json-ambiguity-tester/SKILL.md +175 -0
- package/skills/k8s-container-escaper/SKILL.md +74 -0
- package/skills/key-management-lifecycle-analyst/SKILL.md +92 -0
- package/skills/kill-switch-engineer/SKILL.md +205 -0
- package/skills/linddun-privacy-analyst/SKILL.md +196 -0
- package/skills/logic-race-fuzzer/SKILL.md +67 -0
- package/skills/mobile-api-network-attacker/SKILL.md +81 -0
- package/skills/mobile-binary-hardener/SKILL.md +199 -0
- package/skills/mobile-security-specialist/SKILL.md +124 -0
- package/skills/mobile-webview-auditor/SKILL.md +200 -0
- package/skills/model-extraction-attacker/SKILL.md +68 -0
- package/skills/multipart-abuse-tester/SKILL.md +146 -0
- package/skills/oauth-pkce-specialist/SKILL.md +191 -0
- package/skills/parser-exhaustion-tester/SKILL.md +177 -0
- package/skills/pentest-infra/SKILL.md +69 -0
- package/skills/pentest-social/SKILL.md +72 -0
- package/skills/pentest-team/SKILL.md +126 -0
- package/skills/pentest-web-api/SKILL.md +71 -0
- package/skills/privacy-flow-analyst/SKILL.md +70 -0
- package/skills/prompt-injection-specialist/SKILL.md +76 -0
- package/skills/quantum-migration-planner/SKILL.md +184 -0
- package/skills/rag-poisoning-specialist/SKILL.md +71 -0
- package/skills/registry-mirror-enforcer/SKILL.md +142 -0
- package/skills/rotation-validation-agent/SKILL.md +188 -0
- package/skills/samm-assessor/SKILL.md +168 -0
- package/skills/secrets-mask-bypass-tester/SKILL.md +167 -0
- package/skills/senior-security-engineer/SKILL.md +42 -12
- package/skills/serialization-memory-attacker/SKILL.md +78 -0
- package/skills/session-timeout-tester/SKILL.md +197 -0
- package/skills/slsa-level3-enforcer/SKILL.md +185 -0
- package/skills/slsa-provenance-enforcer/SKILL.md +181 -0
- package/skills/ssrf-detection-validator/SKILL.md +229 -0
- package/skills/step-up-auth-enforcer/SKILL.md +176 -0
- package/skills/stride-pasta-analyst/SKILL.md +72 -0
- package/skills/supply-chain-devsecops/SKILL.md +82 -0
- package/skills/threat-infrastructure-analyst/SKILL.md +167 -0
- package/skills/threat-modeler/SKILL.md +116 -0
- package/skills/tls-certificate-auditor/SKILL.md +76 -0
- package/skills/token-reuse-detector/SKILL.md +203 -0
- package/skills/trike-risk-modeler/SKILL.md +139 -0
- package/skills/unicode-homograph-tester/SKILL.md +179 -0
- package/skills/waf-rule-lifecycle-agent/SKILL.md +213 -0
- package/skills/webhook-security-tester/SKILL.md +184 -0
- package/skills/zero-trust-architect/SKILL.md +211 -0
package/dist/mcp/server.js
CHANGED
|
@@ -8,22 +8,39 @@ import { runPrGate } from "../gate/policy.js";
|
|
|
8
8
|
import { readFileSafe } from "../repo/fs.js";
|
|
9
9
|
import { searchRepo } from "../repo/search.js";
|
|
10
10
|
import { createReviewAttestation, createReviewRun, readReviewRun, updateReviewStep } from "../review/store.js";
|
|
11
|
+
import { createAgentRun, CreateAgentRunSchema, updateAgentStatus, UpdateAgentStatusSchema, mergeAgentFindings, MergeAgentFindingsSchema, ensureSkill, EnsureSkillSchema, readAgentMemory, ReadAgentMemorySchema, writeAgentMemory, WriteAgentMemorySchema, checkUpdates, CheckUpdatesSchema, applyUpdates, ApplyUpdatesSchema, verifySkillCoverage, VerifySkillCoverageSchema } from "./orchestration.js";
|
|
12
|
+
import { recordOutcome, RecordOutcomeParams, getRouting, GetRoutingParams, getPatternReport } from "./learning.js";
|
|
13
|
+
import { getModelForTask, GetModelForTaskParams, trackUsage, TrackUsageParams, getBudgetStatus, getProviderHealth, recordProviderFailure, RecordProviderFailureParams, RecordProviderFailureSchema, resetProviderCircuit, ResetProviderCircuitParams, ResetProviderCircuitSchema } from "./model-router.js";
|
|
14
|
+
import { initChain, InitChainParams, attestAgent, AttestAgentParams, verifyChain, VerifyChainParams, getChain, GetChainParams } from "./audit-chain.js";
|
|
11
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
16
|
const PKG_ROOT = resolve(__dirname, "../..");
|
|
13
17
|
const PROMPTS_DIR = join(PKG_ROOT, "prompts");
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return readFileSync(path, "utf-8");
|
|
18
|
+
// Read version from package.json rather than hardcoding it (M1 fix — CWE-1007).
|
|
19
|
+
const _pkgVersion = (() => {
|
|
20
|
+
try {
|
|
21
|
+
const raw = readFileSync(join(PKG_ROOT, "package.json"), "utf-8");
|
|
22
|
+
return JSON.parse(raw).version ?? "0.0.0";
|
|
20
23
|
}
|
|
21
|
-
|
|
24
|
+
catch {
|
|
25
|
+
return "0.0.0";
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
28
|
+
// Lazily load the security prompt on first use rather than at server startup.
|
|
29
|
+
// This avoids injecting ~19K tokens into every session that doesn't call a
|
|
30
|
+
// security tool (e.g. non-security MCP usage in the same editor).
|
|
31
|
+
let _securityPromptCache = null;
|
|
32
|
+
function getSecurityPrompt() {
|
|
33
|
+
if (_securityPromptCache !== null)
|
|
34
|
+
return _securityPromptCache;
|
|
35
|
+
const path = join(PROMPTS_DIR, "SECURITY_PROMPT.md");
|
|
36
|
+
_securityPromptCache = existsSync(path)
|
|
37
|
+
? readFileSync(path, "utf-8")
|
|
38
|
+
: `[security-mcp] Prompt file not found. Run "npm run build" from the package root.`;
|
|
39
|
+
return _securityPromptCache;
|
|
22
40
|
}
|
|
23
|
-
const SECURITY_PROMPT = loadPromptFile("SECURITY_PROMPT.md");
|
|
24
41
|
const server = new McpServer({
|
|
25
42
|
name: "security-mcp",
|
|
26
|
-
version:
|
|
43
|
+
version: _pkgVersion
|
|
27
44
|
});
|
|
28
45
|
const tool = server.tool.bind(server);
|
|
29
46
|
// ---------------------------------------------------------------------------
|
|
@@ -91,9 +108,14 @@ tool("security.start_review", "Start a stateful security review run, lock the sc
|
|
|
91
108
|
]
|
|
92
109
|
});
|
|
93
110
|
}));
|
|
111
|
+
// CWE-200: restrict to SECURITY_-prefixed names so callers cannot probe arbitrary env vars
|
|
112
|
+
const ATTEST_ENV_VAR_RE = /^SECURITY_[A-Z][A-Z0-9_]{0,63}$/;
|
|
94
113
|
const AttestReviewParams = {
|
|
95
114
|
runId: z.string().uuid().describe("Security review run ID."),
|
|
96
|
-
signatureEnvVar: z.string()
|
|
115
|
+
signatureEnvVar: z.string()
|
|
116
|
+
.regex(ATTEST_ENV_VAR_RE, "signatureEnvVar must be a SECURITY_-prefixed env var name (e.g. SECURITY_ATTEST_KEY)")
|
|
117
|
+
.optional()
|
|
118
|
+
.describe("Optional SECURITY_-prefixed environment variable containing an HMAC key for attestation signing.")
|
|
97
119
|
};
|
|
98
120
|
const AttestReviewSchema = z.object(AttestReviewParams);
|
|
99
121
|
tool("security.attest_review", "Generate a security review attestation with integrity hash and optional HMAC signature.", AttestReviewParams, safeTool(async (args, _extra) => {
|
|
@@ -213,7 +235,7 @@ tool("security.get_system_prompt", "Return the full security engineering system
|
|
|
213
235
|
"**10% explanation:** One line — what was wrong, what attack it prevents, which framework " +
|
|
214
236
|
"control applies (OWASP, ATT&CK, NIST). Then move on.\n\n" +
|
|
215
237
|
"---\n\n";
|
|
216
|
-
let prompt = OPERATING_MANDATE +
|
|
238
|
+
let prompt = OPERATING_MANDATE + getSecurityPrompt();
|
|
217
239
|
// Append a project-specific scope section if any context was provided
|
|
218
240
|
if (stack ?? cloud ?? payment_processor) {
|
|
219
241
|
const scopeLines = [
|
|
@@ -1371,7 +1393,7 @@ server.prompt("security-engineer", "Activate the security-mcp system prompt. Ope
|
|
|
1371
1393
|
role: "user",
|
|
1372
1394
|
content: {
|
|
1373
1395
|
type: "text",
|
|
1374
|
-
text:
|
|
1396
|
+
text: getSecurityPrompt()
|
|
1375
1397
|
}
|
|
1376
1398
|
}
|
|
1377
1399
|
]
|
|
@@ -1392,6 +1414,132 @@ server.prompt("threat-model-template", "Generate a blank STRIDE + PASTA + MITRE
|
|
|
1392
1414
|
]
|
|
1393
1415
|
}));
|
|
1394
1416
|
// ---------------------------------------------------------------------------
|
|
1417
|
+
// Orchestration tools — multi-agent coordination
|
|
1418
|
+
// ---------------------------------------------------------------------------
|
|
1419
|
+
tool("orchestration.create_agent_run", "Initialise a multi-agent orchestration run. Creates the agent-run directory and manifest. Call after security.start_review.", CreateAgentRunSchema.shape, safeTool(async (args, _extra) => {
|
|
1420
|
+
const parsed = CreateAgentRunSchema.parse(args);
|
|
1421
|
+
const result = await createAgentRun(parsed);
|
|
1422
|
+
return asTextResponse(result);
|
|
1423
|
+
}));
|
|
1424
|
+
tool("orchestration.update_agent_status", "Update an agent's lifecycle status (running/completed/completed_partial/failed). Called by each agent at start and end.", UpdateAgentStatusSchema.shape, safeTool(async (args, _extra) => {
|
|
1425
|
+
const parsed = UpdateAgentStatusSchema.parse(args);
|
|
1426
|
+
const result = await updateAgentStatus(parsed);
|
|
1427
|
+
return asTextResponse(result);
|
|
1428
|
+
}));
|
|
1429
|
+
tool("orchestration.merge_agent_findings", "Merge and deduplicate findings from all agents. Sorts by severity (CRITICAL first). Hooks into the attestation flow via updateReviewStep. Call in Phase 3 after all agents complete.", MergeAgentFindingsSchema.shape, safeTool(async (args, _extra) => {
|
|
1430
|
+
const parsed = MergeAgentFindingsSchema.parse(args);
|
|
1431
|
+
const result = await mergeAgentFindings(parsed);
|
|
1432
|
+
return asTextResponse(result);
|
|
1433
|
+
}));
|
|
1434
|
+
tool("orchestration.ensure_skill", "Download a skill from the skills registry if it is not already installed or if it is outdated. Uses the skills-manifest.json registry. Requires internet access.", EnsureSkillSchema.shape, safeTool(async (args, _extra) => {
|
|
1435
|
+
const parsed = EnsureSkillSchema.parse(args);
|
|
1436
|
+
const result = await ensureSkill(parsed);
|
|
1437
|
+
return asTextResponse(result);
|
|
1438
|
+
}));
|
|
1439
|
+
tool("orchestration.read_agent_memory", "Read the persistent memory files for a named agent: patterns, false-positives, remediations, intel, and errors.", ReadAgentMemorySchema.shape, safeTool(async (args, _extra) => {
|
|
1440
|
+
const parsed = ReadAgentMemorySchema.parse(args);
|
|
1441
|
+
const result = await readAgentMemory(parsed);
|
|
1442
|
+
return asTextResponse(result);
|
|
1443
|
+
}));
|
|
1444
|
+
tool("orchestration.write_agent_memory", "Append new entries to an agent's persistent memory (patterns, false-positives, remediations, intel). Memory persists across runs and is used to calibrate findings.", WriteAgentMemorySchema.shape, safeTool(async (args, _extra) => {
|
|
1445
|
+
const parsed = WriteAgentMemorySchema.parse(args);
|
|
1446
|
+
const result = await writeAgentMemory(parsed);
|
|
1447
|
+
return asTextResponse(result);
|
|
1448
|
+
}));
|
|
1449
|
+
tool("orchestration.check_updates", "Check the npm registry and skills manifest for available updates to security-mcp and installed skills.", CheckUpdatesSchema.shape, safeTool(async (args, _extra) => {
|
|
1450
|
+
const parsed = CheckUpdatesSchema.parse(args);
|
|
1451
|
+
const result = await checkUpdates(parsed);
|
|
1452
|
+
return asTextResponse(result);
|
|
1453
|
+
}));
|
|
1454
|
+
tool("orchestration.apply_updates", "Return update commands (choice: manual) or instructions for the agent to run them (choice: auto).", ApplyUpdatesSchema.shape, safeTool(async (args, _extra) => {
|
|
1455
|
+
const parsed = ApplyUpdatesSchema.parse(args);
|
|
1456
|
+
const result = await applyUpdates(parsed);
|
|
1457
|
+
return asTextResponse(result);
|
|
1458
|
+
}));
|
|
1459
|
+
tool("orchestration.verify_skill_coverage", "Verify that all 24 SKILL.md sections have been covered by at least one agent in this run. Returns uncovered sections and a coverage percentage.", VerifySkillCoverageSchema.shape, safeTool(async (args, _extra) => {
|
|
1460
|
+
const parsed = VerifySkillCoverageSchema.parse(args);
|
|
1461
|
+
const result = await verifySkillCoverage(parsed);
|
|
1462
|
+
return asTextResponse(result);
|
|
1463
|
+
}));
|
|
1464
|
+
// ---------------------------------------------------------------------------
|
|
1465
|
+
// Learning engine tools
|
|
1466
|
+
// ---------------------------------------------------------------------------
|
|
1467
|
+
tool("security.record_outcome", "Record the outcome of an agent resolving (or failing to resolve) a security finding. Feeds the pattern memory engine so the routing system learns which agents perform best on which finding types.", RecordOutcomeParams, safeTool(async (args, _extra) => {
|
|
1468
|
+
const result = await recordOutcome(args);
|
|
1469
|
+
return asTextResponse(result);
|
|
1470
|
+
}));
|
|
1471
|
+
tool("security.get_routing", "Get the routing recommendation for a finding type. Returns which agent to route to, the success rate, and whether to escalate. Requires findingId in SCREAMING_SNAKE_CASE.", GetRoutingParams, safeTool(async (args, _extra) => {
|
|
1472
|
+
const { findingId } = args;
|
|
1473
|
+
const result = await getRouting(findingId);
|
|
1474
|
+
return asTextResponse(result);
|
|
1475
|
+
}));
|
|
1476
|
+
tool("security.pattern_report", "Generate a full report of learned patterns and agent performance. Shows high-confidence routing decisions, low-confidence escalations, and top agents by finding type coverage.", {}, safeTool(async (_args, _extra) => {
|
|
1477
|
+
const result = await getPatternReport();
|
|
1478
|
+
return asTextResponse(result);
|
|
1479
|
+
}));
|
|
1480
|
+
// ---------------------------------------------------------------------------
|
|
1481
|
+
// Model router tools
|
|
1482
|
+
// ---------------------------------------------------------------------------
|
|
1483
|
+
tool("security.get_model_for_task", "Get the cheapest healthy model meeting the capability requirement for a given task type. " +
|
|
1484
|
+
"Multi-provider: routes across Claude, GPT, Gemini, Cohere, and local Llama. " +
|
|
1485
|
+
"Read-only/pattern tasks → cheapest light-tier model. Reasoning/remediation → cheapest standard-tier model. " +
|
|
1486
|
+
"Respects per-provider circuit breakers (auto-failover on failure). Returns provider, model ID, cost, and rationale.", GetModelForTaskParams, safeTool(async (args, _extra) => {
|
|
1487
|
+
const { taskType, agentName, agentRunId } = args;
|
|
1488
|
+
const result = await getModelForTask(taskType, { agentName, agentRunId });
|
|
1489
|
+
return asTextResponse(result);
|
|
1490
|
+
}));
|
|
1491
|
+
tool("security.track_usage", "Record actual token usage after a model call completes. Updates running budget total and per-provider spend breakdown. " +
|
|
1492
|
+
"Also resets the circuit breaker failure count for a successful provider call.", TrackUsageParams, safeTool(async (args, _extra) => {
|
|
1493
|
+
await trackUsage(args);
|
|
1494
|
+
return asTextResponse({ tracked: true });
|
|
1495
|
+
}));
|
|
1496
|
+
tool("security.model_budget_status", "Return current model budget status: total spend, remaining budget, utilization percentage, " +
|
|
1497
|
+
"per-tier call counts, per-task-type breakdown, and per-provider cost breakdown.", {}, safeTool(async (_args, _extra) => {
|
|
1498
|
+
const result = await getBudgetStatus();
|
|
1499
|
+
return asTextResponse(result);
|
|
1500
|
+
}));
|
|
1501
|
+
tool("security.get_provider_health", "Return circuit breaker health state for all LLM providers (Claude, GPT, Gemini, Cohere, local). " +
|
|
1502
|
+
"Shows consecutive failures, circuit open/closed status, and cooldown expiry. " +
|
|
1503
|
+
"Use to diagnose why a provider is being skipped in smart routing.", {}, safeTool(async (_args, _extra) => {
|
|
1504
|
+
const result = await getProviderHealth();
|
|
1505
|
+
return asTextResponse(result);
|
|
1506
|
+
}));
|
|
1507
|
+
tool("security.record_provider_failure", "Record a provider failure (connection error, auth error, rate limit). " +
|
|
1508
|
+
"Increments consecutive failure count. Opens circuit breaker after 3 consecutive failures for 60 seconds. " +
|
|
1509
|
+
"Call this when a model API call fails so the router skips that provider on next routing decision.", RecordProviderFailureParams, safeTool(async (args, _extra) => {
|
|
1510
|
+
const { provider } = RecordProviderFailureSchema.parse(args);
|
|
1511
|
+
await recordProviderFailure(provider);
|
|
1512
|
+
return asTextResponse({ recorded: true, provider });
|
|
1513
|
+
}));
|
|
1514
|
+
tool("security.reset_provider_circuit", "Manually close (reset) the circuit breaker for a provider. " +
|
|
1515
|
+
"Use after confirming a provider is back online or to override an automatic failover during incident recovery.", ResetProviderCircuitParams, safeTool(async (args, _extra) => {
|
|
1516
|
+
const { provider } = ResetProviderCircuitSchema.parse(args);
|
|
1517
|
+
await resetProviderCircuit(provider);
|
|
1518
|
+
return asTextResponse({ reset: true, provider });
|
|
1519
|
+
}));
|
|
1520
|
+
// ---------------------------------------------------------------------------
|
|
1521
|
+
// Audit chain tools
|
|
1522
|
+
// ---------------------------------------------------------------------------
|
|
1523
|
+
tool("security.init_chain", "Initialise the tamper-evident attestation chain for an agent run. Creates the genesis block. Must be called before attestAgent. Idempotent.", InitChainParams, safeTool(async (args, _extra) => {
|
|
1524
|
+
const { agentRunId } = args;
|
|
1525
|
+
const result = await initChain(agentRunId);
|
|
1526
|
+
return asTextResponse(result);
|
|
1527
|
+
}));
|
|
1528
|
+
tool("security.attest_agent", "Append a tamper-evident attestation for an agent's findings to the run chain. Links to the previous attestation via SHA-256 hash chain. Call after every agent completes.", AttestAgentParams, safeTool(async (args, _extra) => {
|
|
1529
|
+
const result = await attestAgent(args);
|
|
1530
|
+
return asTextResponse(result);
|
|
1531
|
+
}));
|
|
1532
|
+
tool("security.verify_chain", "Verify the integrity of the attestation chain for an agent run. Recomputes all SHA-256 hashes and checks parent linkage. Returns valid: true only if every link is intact.", VerifyChainParams, safeTool(async (args, _extra) => {
|
|
1533
|
+
const { agentRunId } = args;
|
|
1534
|
+
const result = await verifyChain(agentRunId);
|
|
1535
|
+
return asTextResponse(result);
|
|
1536
|
+
}));
|
|
1537
|
+
tool("security.get_chain", "Read the full attestation chain for an agent run for inspection. Returns all links with their hashes, finding counts, and timestamps.", GetChainParams, safeTool(async (args, _extra) => {
|
|
1538
|
+
const { agentRunId } = args;
|
|
1539
|
+
const result = await getChain(agentRunId);
|
|
1540
|
+
return asTextResponse(result);
|
|
1541
|
+
}));
|
|
1542
|
+
// ---------------------------------------------------------------------------
|
|
1395
1543
|
// Server startup
|
|
1396
1544
|
// ---------------------------------------------------------------------------
|
|
1397
1545
|
export async function main() {
|
package/dist/repo/search.js
CHANGED
|
@@ -47,13 +47,11 @@ export async function searchRepo(opts) {
|
|
|
47
47
|
"**/.git/**",
|
|
48
48
|
"**/dist/**",
|
|
49
49
|
"**/.claude/**",
|
|
50
|
-
// Exclude
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
"src/
|
|
55
|
-
"src/cli/**",
|
|
56
|
-
"prompts/**"
|
|
50
|
+
// Exclude detection-engine source — these files define the regex patterns that
|
|
51
|
+
// the checks search for, so they would trigger their own scanners. When deployed
|
|
52
|
+
// as an npm package the compiled dist/ is what runs; src/ lives in node_modules
|
|
53
|
+
// which is excluded above. This ignore only affects the tool's self-scan.
|
|
54
|
+
"src/gate/**"
|
|
57
55
|
]
|
|
58
56
|
});
|
|
59
57
|
const re = opts.isRegex ? compileUserRegex(opts.query) : null;
|
package/dist/review/store.js
CHANGED
|
@@ -34,10 +34,15 @@ function computeAllCriticalComplete(items) {
|
|
|
34
34
|
.filter((i) => i.critical)
|
|
35
35
|
.every((i) => i.status === "completed" || i.status === "na");
|
|
36
36
|
}
|
|
37
|
+
// CWE-22: surface names used as filenames — restrict to safe alphanumeric slug
|
|
38
|
+
const SAFE_SURFACE_RE = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
37
39
|
/**
|
|
38
40
|
* Initialize a checklist for a run from the surface template.
|
|
39
41
|
*/
|
|
40
42
|
export async function initChecklist(runId, surface) {
|
|
43
|
+
if (!SAFE_SURFACE_RE.test(surface)) {
|
|
44
|
+
throw new Error(`Invalid surface name "${surface}"`);
|
|
45
|
+
}
|
|
41
46
|
// Load template from defaults/checklists/{surface}.json
|
|
42
47
|
let template;
|
|
43
48
|
try {
|
|
@@ -172,7 +177,17 @@ export async function createReviewRun(opts) {
|
|
|
172
177
|
await writeJson(reviewPath(run.id), run);
|
|
173
178
|
return run;
|
|
174
179
|
}
|
|
180
|
+
// CWE-22: validate UUID format before using runId as a filename component.
|
|
181
|
+
// Defense-in-depth — the MCP tool schemas also validate, but the function must
|
|
182
|
+
// be safe regardless of call site.
|
|
183
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
184
|
+
function assertRunId(runId) {
|
|
185
|
+
if (!runId || !UUID_RE.test(runId)) {
|
|
186
|
+
throw new Error(`Invalid runId "${runId}" — must be a UUID`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
175
189
|
export async function readReviewRun(runId) {
|
|
190
|
+
assertRunId(runId);
|
|
176
191
|
const raw = await readFile(reviewPath(runId), "utf-8");
|
|
177
192
|
return JSON.parse(raw);
|
|
178
193
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the multi-agent orchestration system.
|
|
3
|
+
*
|
|
4
|
+
* Agent runs are coordinated via a manifest stored at
|
|
5
|
+
* .mcp/agent-runs/{agentRunId}/manifest.json. Each specialist agent
|
|
6
|
+
* writes its findings to a dedicated file in that directory.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "security-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
65
65
|
"execa": "^9.5.2",
|
|
66
66
|
"fast-glob": "^3.3.3",
|
|
67
|
-
"picomatch": "^
|
|
67
|
+
"picomatch": "^4.0.4",
|
|
68
68
|
"zod": "^3.24.1"
|
|
69
69
|
},
|
|
70
70
|
"overrides": {
|
|
@@ -74,11 +74,11 @@
|
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@eslint/js": "^9.22.0",
|
|
76
76
|
"@types/node": "^22.13.5",
|
|
77
|
-
"@types/picomatch": "^
|
|
77
|
+
"@types/picomatch": "^4.0.2",
|
|
78
78
|
"eslint": "^9.22.0",
|
|
79
79
|
"globals": "^16.0.0",
|
|
80
|
-
"typescript
|
|
81
|
-
"typescript": "^
|
|
80
|
+
"typescript": "^5.7.3",
|
|
81
|
+
"typescript-eslint": "^8.26.0"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=20"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: AGENT_NAME
|
|
3
|
+
description: >
|
|
4
|
+
One-sentence description of what this agent does and which policy section(s) it covers.
|
|
5
|
+
Include the SKILL.md section reference (e.g. §6, §12.1) and key attack surface.
|
|
6
|
+
user-invocable: false
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Edit, WebSearch, WebFetch
|
|
8
|
+
model: haiku | sonnet
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# AGENT_TITLE — Sub-Agent N
|
|
12
|
+
|
|
13
|
+
## IDENTITY
|
|
14
|
+
|
|
15
|
+
You are a specialist who has [past-tense attack scenario in first person — demonstrates adversarial
|
|
16
|
+
expertise]. Every [attack surface] is an attack surface and every [asset] is a target.
|
|
17
|
+
|
|
18
|
+
## MANDATE
|
|
19
|
+
|
|
20
|
+
[One paragraph: what this agent finds, what it fixes, and which policy section it fully covers.
|
|
21
|
+
Always 90% fixing — write the fix, not just the advisory.]
|
|
22
|
+
|
|
23
|
+
Covers: §X, §Y fully. Beyond SKILL.md: [list additional attack surface covered].
|
|
24
|
+
|
|
25
|
+
## LEARNING SIGNAL
|
|
26
|
+
|
|
27
|
+
On every finding resolved, emit:
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"findingId": "FINDING_ID",
|
|
31
|
+
"agentName": "AGENT_NAME",
|
|
32
|
+
"resolved": true | false,
|
|
33
|
+
"remediationTemplate": "one-line description of what was done",
|
|
34
|
+
"falsePositive": false
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
This feeds `security.record_outcome` so the routing engine improves over time.
|
|
38
|
+
|
|
39
|
+
## EXECUTION
|
|
40
|
+
|
|
41
|
+
### Phase 1 — Reconnaissance
|
|
42
|
+
[List specific files, patterns, and tools to examine. Be precise — file globs, regex patterns,
|
|
43
|
+
exact CLI commands. No vague "look for X".]
|
|
44
|
+
|
|
45
|
+
### Phase 2 — Analysis
|
|
46
|
+
[How to determine severity. What conditions make it HIGH vs MEDIUM. Reference specific CVSS
|
|
47
|
+
factors or ATT&CK technique IDs where applicable.]
|
|
48
|
+
|
|
49
|
+
### Phase 3 — Remediation (90%)
|
|
50
|
+
[Produce the fix. Write the code, the config, the policy. Not pseudocode. Production-ready.]
|
|
51
|
+
|
|
52
|
+
### Phase 4 — Verification
|
|
53
|
+
[How to verify the fix works. Specific test commands, expected output, regression tests to add.]
|
|
54
|
+
|
|
55
|
+
## STACK-AWARE PATTERNS
|
|
56
|
+
|
|
57
|
+
- **Next.js / App Router detected:** [Specific patterns to check]
|
|
58
|
+
- **GCP detected:** [Specific GCP resource paths and policies]
|
|
59
|
+
- **Stripe detected:** [Payment-specific checks]
|
|
60
|
+
- **AI/LLM detected:** [Prompt/model-specific checks]
|
|
61
|
+
- **Mobile detected:** [iOS/Android-specific checks]
|
|
62
|
+
|
|
63
|
+
## INTERNET USAGE
|
|
64
|
+
|
|
65
|
+
If internet permitted:
|
|
66
|
+
- [Specific URLs or search queries to validate findings against live threat intel]
|
|
67
|
+
- Check CISA KEV: `https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json`
|
|
68
|
+
- Search for relevant CVEs: `site:nvd.nist.gov CVE [technology]`
|
|
69
|
+
|
|
70
|
+
## COMPLIANCE MAPPING
|
|
71
|
+
|
|
72
|
+
Every finding must include:
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"complianceImpact": {
|
|
76
|
+
"pciDss": ["Req X.Y"],
|
|
77
|
+
"soc2": ["CC6.1"],
|
|
78
|
+
"nist80053": ["AC-2", "IA-5"],
|
|
79
|
+
"iso27001": ["A.9.4"],
|
|
80
|
+
"owasp": ["A01:2021"]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## OUTPUT FORMAT
|
|
86
|
+
|
|
87
|
+
`AgentFinding[]` array. Each finding must include:
|
|
88
|
+
- `id`: SCREAMING_SNAKE_CASE identifier (e.g. `FINDING_CATEGORY_SPECIFIC_ISSUE`)
|
|
89
|
+
- `title`: one-line description
|
|
90
|
+
- `severity`: CRITICAL | HIGH | MEDIUM | LOW
|
|
91
|
+
- `cwe`: CWE-NNN
|
|
92
|
+
- `attackTechnique`: MITRE ATT&CK technique ID (e.g. T1078)
|
|
93
|
+
- `files`: affected file paths
|
|
94
|
+
- `evidence`: specific lines of code or config that confirm the finding
|
|
95
|
+
- `remediated`: true if the fix was written inline
|
|
96
|
+
- `remediationSummary`: what was changed
|
|
97
|
+
- `requiredActions`: ordered list of actions if not auto-remediated
|
|
98
|
+
- `complianceImpact`: framework mappings
|
|
99
|
+
- `beyondSkillMd`: true if this finding goes beyond the SKILL.md mandate
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: advanced-dos-tester
|
|
3
|
+
description: >
|
|
4
|
+
Tests advanced DoS: slowloris, HTTP/2 rapid reset (CVE-2023-44487), QUIC amplification,
|
|
5
|
+
TCP SYN flood, application-layer amplification via cache, and cost-amplification attacks on cloud APIs.
|
|
6
|
+
Covers §8 (availability), beyond basic rate limiting. Key surfaces: infra, API.
|
|
7
|
+
user-invocable: false
|
|
8
|
+
allowed-tools: Read, Glob, Grep, Bash, Edit, WebSearch, WebFetch
|
|
9
|
+
model: sonnet
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Advanced DoS Tester — Sub-Agent
|
|
13
|
+
|
|
14
|
+
## IDENTITY
|
|
15
|
+
|
|
16
|
+
I have exploited HTTP/2 Rapid Reset (CVE-2023-44487) to generate 390 million requests per second from a single client. I have found cloud cost amplification attacks where $1 of attacker spend generates $500 of victim cloud costs via Lambda cold-start flooding. I understand Slowloris, R.U.D.Y., application-layer amplification, and every layer of the DoS kill chain beyond volumetric.
|
|
17
|
+
|
|
18
|
+
## MANDATE
|
|
19
|
+
|
|
20
|
+
Audit for advanced DoS vectors beyond rate limiting: HTTP/2 rapid reset, connection exhaustion, slow read attacks, application-layer amplification, and cloud cost amplification. Implement: connection limits, request timeout enforcement, HTTP/2 stream limits, and cloud budget alerts.
|
|
21
|
+
|
|
22
|
+
Covers: §8.4 (advanced DoS resilience) fully.
|
|
23
|
+
Beyond SKILL.md: HTTP/2 Rapid Reset, QUIC amplification, WebSocket ping flood, gRPC streaming DoS.
|
|
24
|
+
|
|
25
|
+
## LEARNING SIGNAL
|
|
26
|
+
|
|
27
|
+
On every finding resolved, emit:
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"findingId": "ADVANCED_DOS_FINDING_ID",
|
|
31
|
+
"agentName": "advanced-dos-tester",
|
|
32
|
+
"resolved": true,
|
|
33
|
+
"remediationTemplate": "one-line description of what was done",
|
|
34
|
+
"falsePositive": false
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## EXECUTION
|
|
39
|
+
|
|
40
|
+
### Phase 1 — Reconnaissance
|
|
41
|
+
|
|
42
|
+
- Check HTTP/2 configuration: `http2|h2|HTTP/2` in Nginx/Caddy config, `http2Settings` in Node.js server
|
|
43
|
+
- Check connection/stream limits: `maxConnections|connectionTimeout|keepAliveTimeout|headersTimeout`
|
|
44
|
+
- Grep: `WebSocket|ws\.|socket\.io` — WebSocket ping flood risk
|
|
45
|
+
- Check cloud budget alerts: `aws_budgets_budget|google_billing_budget|azure_consumption_budget` in IaC
|
|
46
|
+
- Check Lambda/Cloud Function concurrency limits: `reservedConcurrentExecutions|maxInstances`
|
|
47
|
+
- Grep: `cache.*set|redis\.set|memcached\.set` near computationally expensive operations — cache stampede risk
|
|
48
|
+
|
|
49
|
+
### Phase 2 — Analysis
|
|
50
|
+
|
|
51
|
+
**CRITICAL**:
|
|
52
|
+
- HTTP/2 enabled without stream count limit — Rapid Reset (CVE-2023-44487) vulnerability
|
|
53
|
+
- No cloud budget alert — cost amplification attack runs unchecked
|
|
54
|
+
|
|
55
|
+
**HIGH**:
|
|
56
|
+
- No keep-alive timeout — Slowloris: attacker holds connections open indefinitely
|
|
57
|
+
- Lambda/Cloud Function without concurrency limit — $10k cloud bill from 1 DoS minute
|
|
58
|
+
- No WebSocket rate limiting per connection — ping flood
|
|
59
|
+
|
|
60
|
+
**MEDIUM**:
|
|
61
|
+
- Cache stampede: expensive computation with no mutex/lock on cache miss
|
|
62
|
+
- gRPC streaming without timeout — server holds streams open
|
|
63
|
+
|
|
64
|
+
### Phase 3 — Remediation (90%)
|
|
65
|
+
|
|
66
|
+
**HTTP/2 Rapid Reset mitigation (Node.js HTTP/2 server):**
|
|
67
|
+
```typescript
|
|
68
|
+
import { createSecureServer, constants } from "node:http2";
|
|
69
|
+
|
|
70
|
+
const server = createSecureServer({
|
|
71
|
+
key: tlsKey,
|
|
72
|
+
cert: tlsCert,
|
|
73
|
+
settings: {
|
|
74
|
+
// Limit concurrent streams per connection
|
|
75
|
+
maxConcurrentStreams: 100,
|
|
76
|
+
// Limit header table size
|
|
77
|
+
headerTableSize: 4096
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Limit RST_STREAM rate (Rapid Reset mitigation)
|
|
82
|
+
const rstCounts = new Map<string, { count: number; resetAt: number }>();
|
|
83
|
+
|
|
84
|
+
server.on("session", (session) => {
|
|
85
|
+
session.on("stream", (_stream, headers) => {
|
|
86
|
+
const ip = session.socket?.remoteAddress ?? "unknown";
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
const entry = rstCounts.get(ip) ?? { count: 0, resetAt: now + 1000 };
|
|
89
|
+
|
|
90
|
+
if (now > entry.resetAt) {
|
|
91
|
+
entry.count = 0;
|
|
92
|
+
entry.resetAt = now + 1000;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
entry.count++;
|
|
96
|
+
rstCounts.set(ip, entry);
|
|
97
|
+
|
|
98
|
+
if (entry.count > 500) { // >500 RSTs/sec from same IP
|
|
99
|
+
session.destroy(new Error("RST_STREAM rate limit exceeded"));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Nginx — HTTP/2 and connection limits:**
|
|
106
|
+
```nginx
|
|
107
|
+
http {
|
|
108
|
+
# Keep-alive timeout (prevents Slowloris)
|
|
109
|
+
keepalive_timeout 65s;
|
|
110
|
+
keepalive_requests 100;
|
|
111
|
+
|
|
112
|
+
# Client timeouts
|
|
113
|
+
client_body_timeout 10s;
|
|
114
|
+
client_header_timeout 10s;
|
|
115
|
+
send_timeout 10s;
|
|
116
|
+
|
|
117
|
+
# Limit connections per IP
|
|
118
|
+
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
|
|
119
|
+
limit_conn conn_limit_per_ip 100;
|
|
120
|
+
|
|
121
|
+
# Limit requests per second per IP
|
|
122
|
+
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=100r/s;
|
|
123
|
+
|
|
124
|
+
server {
|
|
125
|
+
# HTTP/2 with stream limits
|
|
126
|
+
listen 443 ssl http2;
|
|
127
|
+
http2_max_concurrent_streams 128;
|
|
128
|
+
|
|
129
|
+
location /api/ {
|
|
130
|
+
limit_req zone=req_limit_per_ip burst=200 nodelay;
|
|
131
|
+
limit_conn conn_limit_per_ip 50;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**AWS Lambda concurrency limit (Terraform):**
|
|
138
|
+
```hcl
|
|
139
|
+
resource "aws_lambda_function" "api" {
|
|
140
|
+
function_name = "api-handler"
|
|
141
|
+
|
|
142
|
+
# REQUIRED: cap concurrent executions to prevent bill amplification
|
|
143
|
+
reserved_concurrent_executions = 100 # Adjust based on expected load
|
|
144
|
+
|
|
145
|
+
# Provisioned concurrency for warm starts (reduces cold-start flood impact)
|
|
146
|
+
# provisioned_concurrent_executions handled separately
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# Budget alert — stop cost amplification before it becomes a problem
|
|
150
|
+
resource "aws_budgets_budget" "monthly" {
|
|
151
|
+
name = "monthly-budget"
|
|
152
|
+
budget_type = "COST"
|
|
153
|
+
limit_amount = "500"
|
|
154
|
+
limit_unit = "USD"
|
|
155
|
+
time_unit = "MONTHLY"
|
|
156
|
+
|
|
157
|
+
notification {
|
|
158
|
+
comparison_operator = "GREATER_THAN"
|
|
159
|
+
threshold = 80
|
|
160
|
+
threshold_type = "PERCENTAGE"
|
|
161
|
+
notification_type = "ACTUAL"
|
|
162
|
+
subscriber_email_addresses = ["oncall@yourcompany.com"]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Cache stampede prevention (mutex):**
|
|
168
|
+
```typescript
|
|
169
|
+
const computationLocks = new Map<string, Promise<unknown>>();
|
|
170
|
+
|
|
171
|
+
export async function getOrCompute<T>(key: string, compute: () => Promise<T>): Promise<T> {
|
|
172
|
+
const cached = await redis.get(key);
|
|
173
|
+
if (cached) return JSON.parse(cached) as T;
|
|
174
|
+
|
|
175
|
+
// Check if computation is already in-flight
|
|
176
|
+
const existing = computationLocks.get(key) as Promise<T> | undefined;
|
|
177
|
+
if (existing) return existing;
|
|
178
|
+
|
|
179
|
+
// Start computation and register lock
|
|
180
|
+
const promise = compute().then((result) => {
|
|
181
|
+
redis.setex(key, 300, JSON.stringify(result));
|
|
182
|
+
computationLocks.delete(key);
|
|
183
|
+
return result;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
computationLocks.set(key, promise);
|
|
187
|
+
return promise;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Phase 4 — Verification
|
|
192
|
+
|
|
193
|
+
- Test keep-alive timeout: open connection, send headers slowly at 1 byte/sec → should timeout in 10s
|
|
194
|
+
- Verify Lambda concurrency limit: check AWS console shows `reserved_concurrent_executions`
|
|
195
|
+
- Confirm budget alert configured: `aws budgets describe-budgets`
|
|
196
|
+
|
|
197
|
+
## COMPLIANCE MAPPING
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"complianceImpact": {
|
|
202
|
+
"pciDss": ["Req 6.4.1"],
|
|
203
|
+
"soc2": ["A1.1", "A1.2"],
|
|
204
|
+
"nist80053": ["SC-5", "CP-2"],
|
|
205
|
+
"iso27001": ["A.12.1.3"],
|
|
206
|
+
"owasp": ["A05:2021"]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## OUTPUT FORMAT
|
|
212
|
+
|
|
213
|
+
`AgentFinding[]` array. Each finding must include:
|
|
214
|
+
- `id`: SCREAMING_SNAKE_CASE (e.g. `ADVANCED_DOS_HTTP2_NO_STREAM_LIMIT`, `ADVANCED_DOS_NO_BUDGET_ALERT`)
|
|
215
|
+
- `title`: one-line description
|
|
216
|
+
- `severity`: CRITICAL | HIGH | MEDIUM | LOW
|
|
217
|
+
- `cwe`: CWE-400 (Resource Exhaustion), CWE-770 (Allocation Without Limits)
|
|
218
|
+
- `attackTechnique`: MITRE ATT&CK T1499.003 (Application Exhaustion Flood)
|
|
219
|
+
- `files`: server config, IaC, Lambda config paths
|
|
220
|
+
- `evidence`: specific missing limit or config
|
|
221
|
+
- `remediated`: true if limits were written inline
|
|
222
|
+
- `remediationSummary`: what was configured
|
|
223
|
+
- `requiredActions`: ordered action list
|
|
224
|
+
- `complianceImpact`: framework mappings
|
|
225
|
+
- `beyondSkillMd`: true if finding goes beyond the SKILL.md mandate
|