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.
Files changed (118) hide show
  1. package/README.md +966 -193
  2. package/defaults/agent-run-schema.json +98 -0
  3. package/dist/ci/pr-gate.js +18 -1
  4. package/dist/cli/install.js +69 -2
  5. package/dist/cli/onboarding.js +82 -11
  6. package/dist/cli/update.js +83 -15
  7. package/dist/gate/checks/ai-redteam.js +83 -59
  8. package/dist/gate/checks/api.js +93 -0
  9. package/dist/gate/checks/ci-pipeline.js +135 -0
  10. package/dist/gate/checks/crypto.js +91 -22
  11. package/dist/gate/checks/database.js +5 -1
  12. package/dist/gate/checks/dependencies.js +297 -2
  13. package/dist/gate/checks/dlp.js +6 -1
  14. package/dist/gate/checks/graphql.js +6 -1
  15. package/dist/gate/checks/k8s.js +229 -181
  16. package/dist/gate/checks/nuclei.js +133 -0
  17. package/dist/gate/checks/runtime.js +75 -8
  18. package/dist/gate/checks/scanners.js +8 -2
  19. package/dist/gate/diff.js +2 -0
  20. package/dist/gate/exceptions.js +6 -1
  21. package/dist/gate/policy.js +47 -4
  22. package/dist/gate/result.js +7 -1
  23. package/dist/mcp/audit-chain.js +253 -0
  24. package/dist/mcp/learning.js +228 -0
  25. package/dist/mcp/model-router.js +544 -0
  26. package/dist/mcp/orchestration.js +604 -0
  27. package/dist/mcp/server.js +160 -12
  28. package/dist/repo/search.js +5 -7
  29. package/dist/review/store.js +15 -0
  30. package/dist/types/agent-run.js +8 -0
  31. package/package.json +5 -5
  32. package/skills/_TEMPLATE/SKILL.md +99 -0
  33. package/skills/advanced-dos-tester/SKILL.md +225 -0
  34. package/skills/agentic-loop-exploiter/SKILL.md +69 -0
  35. package/skills/ai-llm-redteam/SKILL.md +118 -0
  36. package/skills/ai-model-supply-chain-agent/SKILL.md +198 -0
  37. package/skills/algorithm-implementation-reviewer/SKILL.md +85 -0
  38. package/skills/android-penetration-tester/SKILL.md +83 -0
  39. package/skills/anti-replay-tester/SKILL.md +195 -0
  40. package/skills/appsec-code-auditor/SKILL.md +86 -0
  41. package/skills/artifact-integrity-analyst/SKILL.md +68 -0
  42. package/skills/attack-navigator/SKILL.md +64 -0
  43. package/skills/auth-session-hacker/SKILL.md +87 -0
  44. package/skills/aws-penetration-tester/SKILL.md +60 -0
  45. package/skills/azure-penetration-tester/SKILL.md +64 -0
  46. package/skills/binary-auth-validator/SKILL.md +184 -0
  47. package/skills/bot-detection-specialist/SKILL.md +221 -0
  48. package/skills/business-logic-attacker/SKILL.md +76 -0
  49. package/skills/capec-code-mapper/SKILL.md +163 -0
  50. package/skills/cert-pin-rotation-specialist/SKILL.md +200 -0
  51. package/skills/cicd-pipeline-hijacker/SKILL.md +81 -0
  52. package/skills/ciso-orchestrator/SKILL.md +165 -0
  53. package/skills/cloud-infra-specialist/SKILL.md +85 -0
  54. package/skills/compliance-gap-analyst/SKILL.md +77 -0
  55. package/skills/compliance-grc/SKILL.md +148 -0
  56. package/skills/compliance-lifecycle-tracker/SKILL.md +169 -0
  57. package/skills/credential-stuffing-specialist/SKILL.md +192 -0
  58. package/skills/crypto-pki-specialist/SKILL.md +136 -0
  59. package/skills/csa-ccm-mapper/SKILL.md +178 -0
  60. package/skills/csf2-governance-mapper/SKILL.md +159 -0
  61. package/skills/deep-link-fuzzer/SKILL.md +195 -0
  62. package/skills/dependency-confusion-attacker/SKILL.md +78 -0
  63. package/skills/device-integrity-aggregator/SKILL.md +221 -0
  64. package/skills/dos-resilience-tester/SKILL.md +184 -0
  65. package/skills/dread-scorer/SKILL.md +157 -0
  66. package/skills/egress-policy-enforcer/SKILL.md +208 -0
  67. package/skills/evidence-collector/SKILL.md +86 -0
  68. package/skills/file-upload-attacker/SKILL.md +208 -0
  69. package/skills/gcp-penetration-tester/SKILL.md +63 -0
  70. package/skills/git-history-secret-scanner/SKILL.md +182 -0
  71. package/skills/iam-privesc-graph-builder/SKILL.md +216 -0
  72. package/skills/incident-responder/SKILL.md +192 -0
  73. package/skills/injection-specialist/SKILL.md +62 -0
  74. package/skills/ios-security-auditor/SKILL.md +77 -0
  75. package/skills/json-ambiguity-tester/SKILL.md +175 -0
  76. package/skills/k8s-container-escaper/SKILL.md +74 -0
  77. package/skills/key-management-lifecycle-analyst/SKILL.md +92 -0
  78. package/skills/kill-switch-engineer/SKILL.md +205 -0
  79. package/skills/linddun-privacy-analyst/SKILL.md +196 -0
  80. package/skills/logic-race-fuzzer/SKILL.md +67 -0
  81. package/skills/mobile-api-network-attacker/SKILL.md +81 -0
  82. package/skills/mobile-binary-hardener/SKILL.md +199 -0
  83. package/skills/mobile-security-specialist/SKILL.md +124 -0
  84. package/skills/mobile-webview-auditor/SKILL.md +200 -0
  85. package/skills/model-extraction-attacker/SKILL.md +68 -0
  86. package/skills/multipart-abuse-tester/SKILL.md +146 -0
  87. package/skills/oauth-pkce-specialist/SKILL.md +191 -0
  88. package/skills/parser-exhaustion-tester/SKILL.md +177 -0
  89. package/skills/pentest-infra/SKILL.md +69 -0
  90. package/skills/pentest-social/SKILL.md +72 -0
  91. package/skills/pentest-team/SKILL.md +126 -0
  92. package/skills/pentest-web-api/SKILL.md +71 -0
  93. package/skills/privacy-flow-analyst/SKILL.md +70 -0
  94. package/skills/prompt-injection-specialist/SKILL.md +76 -0
  95. package/skills/quantum-migration-planner/SKILL.md +184 -0
  96. package/skills/rag-poisoning-specialist/SKILL.md +71 -0
  97. package/skills/registry-mirror-enforcer/SKILL.md +142 -0
  98. package/skills/rotation-validation-agent/SKILL.md +188 -0
  99. package/skills/samm-assessor/SKILL.md +168 -0
  100. package/skills/secrets-mask-bypass-tester/SKILL.md +167 -0
  101. package/skills/senior-security-engineer/SKILL.md +42 -12
  102. package/skills/serialization-memory-attacker/SKILL.md +78 -0
  103. package/skills/session-timeout-tester/SKILL.md +197 -0
  104. package/skills/slsa-level3-enforcer/SKILL.md +185 -0
  105. package/skills/slsa-provenance-enforcer/SKILL.md +181 -0
  106. package/skills/ssrf-detection-validator/SKILL.md +229 -0
  107. package/skills/step-up-auth-enforcer/SKILL.md +176 -0
  108. package/skills/stride-pasta-analyst/SKILL.md +72 -0
  109. package/skills/supply-chain-devsecops/SKILL.md +82 -0
  110. package/skills/threat-infrastructure-analyst/SKILL.md +167 -0
  111. package/skills/threat-modeler/SKILL.md +116 -0
  112. package/skills/tls-certificate-auditor/SKILL.md +76 -0
  113. package/skills/token-reuse-detector/SKILL.md +203 -0
  114. package/skills/trike-risk-modeler/SKILL.md +139 -0
  115. package/skills/unicode-homograph-tester/SKILL.md +179 -0
  116. package/skills/waf-rule-lifecycle-agent/SKILL.md +213 -0
  117. package/skills/webhook-security-tester/SKILL.md +184 -0
  118. package/skills/zero-trust-architect/SKILL.md +211 -0
@@ -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
- // Load the generalized security prompt at startup.
15
- // Falls back to a short notice if the file has not been built yet.
16
- function loadPromptFile(name) {
17
- const path = join(PROMPTS_DIR, name);
18
- if (existsSync(path)) {
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
- return `[security-mcp] Prompt file not found: ${name}. Run "npm run build" from the package root.`;
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: "1.0.0"
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().optional().describe("Optional environment variable containing an HMAC key for attestation signing.")
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 + SECURITY_PROMPT;
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: SECURITY_PROMPT
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() {
@@ -47,13 +47,11 @@ export async function searchRepo(opts) {
47
47
  "**/.git/**",
48
48
  "**/dist/**",
49
49
  "**/.claude/**",
50
- // Exclude tool-internal filesthey contain detection patterns and remediation
51
- // examples that would trigger their own scanners (false positives in self-scan).
52
- // When deployed as a package, these live in node_modules and are ignored naturally.
53
- "src/gate/**",
54
- "src/mcp/**",
55
- "src/cli/**",
56
- "prompts/**"
50
+ // Exclude detection-engine sourcethese 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;
@@ -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.0",
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": "^3.0.1",
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": "^2.3.4",
77
+ "@types/picomatch": "^4.0.2",
78
78
  "eslint": "^9.22.0",
79
79
  "globals": "^16.0.0",
80
- "typescript-eslint": "^8.26.0",
81
- "typescript": "^5.7.3"
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