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.
Files changed (133) hide show
  1. package/README.md +164 -185
  2. package/defaults/checklists/ai.json +20 -1
  3. package/defaults/checklists/api.json +35 -1
  4. package/defaults/checklists/infra.json +34 -1
  5. package/defaults/checklists/mobile.json +23 -1
  6. package/defaults/checklists/payments.json +15 -1
  7. package/defaults/checklists/web.json +11 -1
  8. package/defaults/control-catalog.json +200 -0
  9. package/defaults/security-policy.json +2 -2
  10. package/dist/cli/index.js +82 -5
  11. package/dist/cli/install.js +36 -6
  12. package/dist/cli/onboarding.js +6 -0
  13. package/dist/gate/baseline.js +82 -7
  14. package/dist/gate/catalog.js +10 -2
  15. package/dist/gate/checks/ai.js +757 -39
  16. package/dist/gate/checks/auth-deep.js +935 -0
  17. package/dist/gate/checks/business-logic.js +751 -0
  18. package/dist/gate/checks/ci-pipeline.js +399 -4
  19. package/dist/gate/checks/crypto.js +423 -2
  20. package/dist/gate/checks/dependencies.js +571 -15
  21. package/dist/gate/checks/graphql.js +201 -19
  22. package/dist/gate/checks/infra.js +246 -1
  23. package/dist/gate/checks/injection-deep.js +848 -0
  24. package/dist/gate/checks/k8s.js +114 -1
  25. package/dist/gate/checks/mobile-android.js +917 -3
  26. package/dist/gate/checks/mobile-ios.js +797 -5
  27. package/dist/gate/checks/required-artifacts.js +194 -0
  28. package/dist/gate/checks/runtime.js +178 -0
  29. package/dist/gate/checks/secrets.js +244 -13
  30. package/dist/gate/checks/supply-chain-deep.js +787 -0
  31. package/dist/gate/checks/web-nextjs.js +572 -48
  32. package/dist/gate/diff.js +17 -5
  33. package/dist/gate/evidence.js +8 -1
  34. package/dist/gate/exceptions.js +131 -9
  35. package/dist/gate/policy.js +282 -129
  36. package/dist/mcp/audit-chain.js +122 -28
  37. package/dist/mcp/auth.js +169 -0
  38. package/dist/mcp/learning.js +129 -4
  39. package/dist/mcp/model-router.js +158 -21
  40. package/dist/mcp/orchestration.js +186 -51
  41. package/dist/mcp/server.js +608 -94
  42. package/dist/repo/fs.js +24 -1
  43. package/dist/repo/search.js +31 -6
  44. package/dist/review/store.js +52 -1
  45. package/package.json +7 -7
  46. package/prompts/SECURITY_PROMPT.md +73 -0
  47. package/skills/_TEMPLATE/SKILL.md +99 -0
  48. package/skills/advanced-dos-tester/SKILL.md +109 -0
  49. package/skills/agentic-loop-exploiter/SKILL.md +368 -0
  50. package/skills/ai-llm-redteam/SKILL.md +104 -0
  51. package/skills/ai-model-supply-chain-agent/SKILL.md +103 -0
  52. package/skills/algorithm-implementation-reviewer/SKILL.md +98 -0
  53. package/skills/android-penetration-tester/SKILL.md +455 -46
  54. package/skills/anti-replay-tester/SKILL.md +106 -0
  55. package/skills/appsec-code-auditor/SKILL.md +120 -0
  56. package/skills/artifact-integrity-analyst/SKILL.md +441 -0
  57. package/skills/attack-navigator/SKILL.md +467 -8
  58. package/skills/auth-session-hacker/SKILL.md +128 -0
  59. package/skills/aws-penetration-tester/SKILL.md +456 -0
  60. package/skills/azure-penetration-tester/SKILL.md +490 -3
  61. package/skills/binary-auth-validator/SKILL.md +111 -0
  62. package/skills/bot-detection-specialist/SKILL.md +109 -0
  63. package/skills/business-logic-attacker/SKILL.md +231 -0
  64. package/skills/capec-code-mapper/SKILL.md +84 -0
  65. package/skills/cert-pin-rotation-specialist/SKILL.md +112 -0
  66. package/skills/cicd-pipeline-hijacker/SKILL.md +405 -0
  67. package/skills/ciso-orchestrator/SKILL.md +454 -43
  68. package/skills/cloud-infra-specialist/SKILL.md +118 -0
  69. package/skills/compliance-gap-analyst/SKILL.md +422 -0
  70. package/skills/compliance-grc/SKILL.md +85 -0
  71. package/skills/compliance-lifecycle-tracker/SKILL.md +84 -0
  72. package/skills/credential-stuffing-specialist/SKILL.md +102 -0
  73. package/skills/crypto-pki-specialist/SKILL.md +87 -0
  74. package/skills/csa-ccm-mapper/SKILL.md +84 -0
  75. package/skills/csf2-governance-mapper/SKILL.md +84 -0
  76. package/skills/deep-link-fuzzer/SKILL.md +109 -0
  77. package/skills/dependency-confusion-attacker/SKILL.md +415 -0
  78. package/skills/device-integrity-aggregator/SKILL.md +108 -0
  79. package/skills/dos-resilience-tester/SKILL.md +97 -0
  80. package/skills/dread-scorer/SKILL.md +84 -0
  81. package/skills/egress-policy-enforcer/SKILL.md +99 -0
  82. package/skills/evidence-collector/SKILL.md +98 -0
  83. package/skills/file-upload-attacker/SKILL.md +109 -0
  84. package/skills/gcp-penetration-tester/SKILL.md +459 -2
  85. package/skills/git-history-secret-scanner/SKILL.md +106 -0
  86. package/skills/iam-privesc-graph-builder/SKILL.md +152 -0
  87. package/skills/incident-responder/SKILL.md +111 -0
  88. package/skills/injection-specialist/SKILL.md +131 -0
  89. package/skills/ios-security-auditor/SKILL.md +282 -0
  90. package/skills/json-ambiguity-tester/SKILL.md +0 -0
  91. package/skills/k8s-container-escaper/SKILL.md +384 -0
  92. package/skills/key-management-lifecycle-analyst/SKILL.md +98 -0
  93. package/skills/kill-switch-engineer/SKILL.md +102 -0
  94. package/skills/linddun-privacy-analyst/SKILL.md +102 -0
  95. package/skills/logic-race-fuzzer/SKILL.md +443 -0
  96. package/skills/mobile-api-network-attacker/SKILL.md +421 -0
  97. package/skills/mobile-binary-hardener/SKILL.md +102 -0
  98. package/skills/mobile-security-specialist/SKILL.md +85 -0
  99. package/skills/mobile-webview-auditor/SKILL.md +96 -0
  100. package/skills/model-extraction-attacker/SKILL.md +219 -0
  101. package/skills/multipart-abuse-tester/SKILL.md +84 -0
  102. package/skills/oauth-pkce-specialist/SKILL.md +104 -0
  103. package/skills/parser-exhaustion-tester/SKILL.md +142 -0
  104. package/skills/pentest-infra/SKILL.md +141 -0
  105. package/skills/pentest-social/SKILL.md +201 -0
  106. package/skills/pentest-team/SKILL.md +134 -0
  107. package/skills/pentest-web-api/SKILL.md +151 -0
  108. package/skills/privacy-flow-analyst/SKILL.md +234 -0
  109. package/skills/prompt-injection-specialist/SKILL.md +394 -0
  110. package/skills/quantum-migration-planner/SKILL.md +96 -0
  111. package/skills/rag-poisoning-specialist/SKILL.md +358 -0
  112. package/skills/registry-mirror-enforcer/SKILL.md +84 -0
  113. package/skills/rotation-validation-agent/SKILL.md +112 -0
  114. package/skills/samm-assessor/SKILL.md +85 -0
  115. package/skills/secrets-mask-bypass-tester/SKILL.md +100 -0
  116. package/skills/senior-security-engineer/SKILL.md +370 -2
  117. package/skills/serialization-memory-attacker/SKILL.md +332 -0
  118. package/skills/session-timeout-tester/SKILL.md +161 -0
  119. package/skills/slsa-level3-enforcer/SKILL.md +112 -0
  120. package/skills/slsa-provenance-enforcer/SKILL.md +102 -0
  121. package/skills/ssrf-detection-validator/SKILL.md +108 -0
  122. package/skills/step-up-auth-enforcer/SKILL.md +84 -0
  123. package/skills/stride-pasta-analyst/SKILL.md +420 -0
  124. package/skills/supply-chain-devsecops/SKILL.md +98 -0
  125. package/skills/threat-infrastructure-analyst/SKILL.md +84 -0
  126. package/skills/threat-modeler/SKILL.md +85 -0
  127. package/skills/tls-certificate-auditor/SKILL.md +573 -18
  128. package/skills/token-reuse-detector/SKILL.md +95 -0
  129. package/skills/trike-risk-modeler/SKILL.md +84 -0
  130. package/skills/unicode-homograph-tester/SKILL.md +84 -0
  131. package/skills/waf-rule-lifecycle-agent/SKILL.md +97 -0
  132. package/skills/webhook-security-tester/SKILL.md +102 -0
  133. package/skills/zero-trust-architect/SKILL.md +109 -0
@@ -5,7 +5,39 @@
5
5
  import { execFile } from "node:child_process";
6
6
  import { promisify } from "node:util";
7
7
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
8
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
8
9
  import { join } from "node:path";
10
+ // ---------------------------------------------------------------------------
11
+ // HMAC integrity helpers — TM-013 fix
12
+ // ---------------------------------------------------------------------------
13
+ // HMAC-SHA256 requires at least 32 bytes (256 bits) per NIST SP 800-107 §5.3.4.
14
+ const HMAC_MIN_KEY_BYTES = 32;
15
+ /**
16
+ * Returns the HMAC key from env, or null if not configured.
17
+ * Throws if the key is present but too short.
18
+ */
19
+ function getHmacKey() {
20
+ const key = process.env["SECURITY_POLICY_HMAC_KEY"];
21
+ if (!key)
22
+ return null;
23
+ if (Buffer.byteLength(key, "utf-8") < HMAC_MIN_KEY_BYTES) {
24
+ throw new Error(`SECURITY_POLICY_HMAC_KEY is too short (${Buffer.byteLength(key, "utf-8")} bytes). ` +
25
+ `Provide at least ${HMAC_MIN_KEY_BYTES} bytes — generate one with: ` +
26
+ `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`);
27
+ }
28
+ return key;
29
+ }
30
+ function signBaseline(json, key) {
31
+ return createHmac("sha256", key).update(json, "utf-8").digest("hex");
32
+ }
33
+ function verifyBaselineHmac(json, stored, key) {
34
+ const expected = createHmac("sha256", key).update(json, "utf-8").digest("hex");
35
+ const storedBuf = Buffer.from(stored, "hex");
36
+ const expectedBuf = Buffer.from(expected, "hex");
37
+ if (storedBuf.length !== expectedBuf.length)
38
+ return false;
39
+ return timingSafeEqual(storedBuf, expectedBuf);
40
+ }
9
41
  const execFileAsync = promisify(execFile);
10
42
  const BASELINE_DIR = join(process.cwd(), ".mcp", "baselines");
11
43
  async function ensureDir(dir) {
@@ -32,37 +64,53 @@ export async function getCommitHash() {
32
64
  /**
33
65
  * Saves a gate result as baseline for the given commit hash.
34
66
  * Also updates the latest baseline copy.
67
+ *
68
+ * TM-013 fix: When SECURITY_POLICY_HMAC_KEY is set, the serialised payload is
69
+ * HMAC-SHA256 signed and the signature is stored in the envelope. Unsigned
70
+ * writes are still permitted when no key is configured (graceful degradation),
71
+ * but loadBaseline will reject a previously-signed file whose signature no
72
+ * longer matches (tamper detection).
35
73
  */
36
74
  export async function saveBaseline(runId, result, commitHash) {
37
75
  await ensureDir(BASELINE_DIR);
38
76
  const payload = { runId, commitHash, savedAt: new Date().toISOString(), result };
39
77
  const json = JSON.stringify(payload, null, 2);
78
+ // Sign if a key is available
79
+ const hmacKey = getHmacKey();
80
+ const envelope = hmacKey
81
+ ? JSON.stringify({ payload, hmacSha256: signBaseline(json, hmacKey) }, null, 2)
82
+ : json;
40
83
  // Write to temp file then rename (atomic)
41
84
  const safehash = commitHash.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
42
85
  const targetPath = join(BASELINE_DIR, `${safehash}.json`);
43
86
  const latestPath = join(BASELINE_DIR, "latest.json");
44
- const tmpPath = `${targetPath}.tmp`;
87
+ const tmpPath = `${targetPath}.${randomBytes(8).toString("hex")}.tmp`;
45
88
  try {
46
- await writeFile(tmpPath, json, "utf-8");
89
+ await writeFile(tmpPath, envelope, "utf-8");
47
90
  await rename(tmpPath, targetPath);
48
91
  }
49
92
  catch {
50
93
  // fallback: write directly
51
- await writeFile(targetPath, json, "utf-8").catch(() => { });
94
+ await writeFile(targetPath, envelope, "utf-8").catch(() => { });
52
95
  }
53
96
  // Update latest (best-effort atomic)
54
- const latestTmp = `${latestPath}.tmp`;
97
+ const latestTmp = `${latestPath}.${randomBytes(8).toString("hex")}.tmp`;
55
98
  try {
56
- await writeFile(latestTmp, json, "utf-8");
99
+ await writeFile(latestTmp, envelope, "utf-8");
57
100
  await rename(latestTmp, latestPath);
58
101
  }
59
102
  catch {
60
- await writeFile(latestPath, json, "utf-8").catch(() => { });
103
+ await writeFile(latestPath, envelope, "utf-8").catch(() => { });
61
104
  }
62
105
  }
63
106
  /**
64
107
  * Loads a baseline by commit hash, or the latest baseline if no hash given.
65
108
  * Returns null if no baseline exists or it's corrupted.
109
+ *
110
+ * TM-013 fix: If the file is stored in the HMAC envelope format AND
111
+ * SECURITY_POLICY_HMAC_KEY is configured, the HMAC is verified before the
112
+ * payload is returned. A tampered baseline (missing or wrong HMAC) is
113
+ * rejected — the gate will run without a baseline rather than trust forged data.
66
114
  */
67
115
  export async function loadBaseline(commitHash) {
68
116
  await ensureDir(BASELINE_DIR);
@@ -76,7 +124,34 @@ export async function loadBaseline(commitHash) {
76
124
  }
77
125
  try {
78
126
  const raw = await readFile(filePath, "utf-8");
79
- const parsed = JSON.parse(raw);
127
+ const top = JSON.parse(raw);
128
+ // Detect envelope format (has both "payload" and "hmacSha256")
129
+ if ("payload" in top && "hmacSha256" in top) {
130
+ const envelope = top;
131
+ const hmacKey = getHmacKey();
132
+ if (hmacKey) {
133
+ // Re-serialise the inner payload the same way saveBaseline did
134
+ const expectedInput = JSON.stringify(envelope.payload, null, 2);
135
+ if (!verifyBaselineHmac(expectedInput, envelope.hmacSha256, hmacKey)) {
136
+ console.error("[baseline] HMAC verification failed — baseline may have been tampered. Ignoring.");
137
+ return null;
138
+ }
139
+ }
140
+ else {
141
+ // Key not configured: we can't verify, but we can warn
142
+ console.warn("[baseline] Baseline is signed but SECURITY_POLICY_HMAC_KEY is not set — skipping HMAC verification.");
143
+ }
144
+ return envelope.payload.result ?? null;
145
+ }
146
+ // Legacy format (unsigned) — parse directly
147
+ const parsed = top;
148
+ const hmacKey = getHmacKey();
149
+ if (hmacKey) {
150
+ // A key is configured but the file is unsigned — reject it to prevent
151
+ // an attacker from stripping the HMAC wrapper to bypass verification.
152
+ console.error("[baseline] SECURITY_POLICY_HMAC_KEY is set but baseline is unsigned — ignoring to prevent tampering bypass.");
153
+ return null;
154
+ }
80
155
  return parsed.result ?? null;
81
156
  }
82
157
  catch {
@@ -23,8 +23,16 @@ async function readJsonWithFallback(relPath, fallbackName) {
23
23
  ".mcp/catalog/control-catalog.json": "SECURITY_GATE_CONTROL_CATALOG"
24
24
  };
25
25
  const overrideEnv = overrideEnvMap[relPath];
26
- if (overrideEnv && process.env[overrideEnv]) {
27
- return await readFile(join(process.cwd(), process.env[overrideEnv]), "utf-8");
26
+ const overridePath = overrideEnv ? process.env[overrideEnv] : undefined;
27
+ if (overridePath) {
28
+ // Guard against path traversal (VULN-003 / CWE-22): resolve() + startsWith() is required;
29
+ // join() alone normalises '..' but does not prevent escape from the project directory.
30
+ const cwd = process.cwd();
31
+ const resolved = resolve(cwd, overridePath);
32
+ if (resolved !== cwd && !resolved.startsWith(cwd + "/")) {
33
+ throw new Error(`${overrideEnv} path escapes the project directory`);
34
+ }
35
+ return await readFile(resolved, "utf-8");
28
36
  }
29
37
  try {
30
38
  return await readFile(join(process.cwd(), relPath), "utf-8");