security-mcp 1.1.4 → 1.3.3

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 (158) hide show
  1. package/README.md +341 -1018
  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/cloud-controls/aws.json +10712 -0
  9. package/defaults/cloud-controls/azure.json +7201 -0
  10. package/defaults/cloud-controls/gcp.json +4061 -0
  11. package/defaults/control-catalog.json +24 -0
  12. package/defaults/security-policy.json +2 -2
  13. package/dist/ci/pr-gate.js +22 -5
  14. package/dist/cli/index.js +73 -2
  15. package/dist/cli/install.js +4 -55
  16. package/dist/cli/onboarding.js +18 -10
  17. package/dist/gate/baseline.js +82 -7
  18. package/dist/gate/catalog.js +10 -2
  19. package/dist/gate/checks/agentic-instructions.js +515 -0
  20. package/dist/gate/checks/ai-governance.js +132 -0
  21. package/dist/gate/checks/ai.js +757 -39
  22. package/dist/gate/checks/auth-deep.js +920 -216
  23. package/dist/gate/checks/business-logic.js +751 -0
  24. package/dist/gate/checks/ci-pipeline.js +399 -4
  25. package/dist/gate/checks/cloud-controls.js +69 -0
  26. package/dist/gate/checks/crypto.js +423 -2
  27. package/dist/gate/checks/data-platform.js +954 -0
  28. package/dist/gate/checks/dependencies.js +582 -15
  29. package/dist/gate/checks/docker-deep.js +1236 -0
  30. package/dist/gate/checks/gitops.js +724 -0
  31. package/dist/gate/checks/graphql.js +201 -19
  32. package/dist/gate/checks/iac.js +1230 -0
  33. package/dist/gate/checks/infra.js +246 -1
  34. package/dist/gate/checks/injection-deep.js +827 -184
  35. package/dist/gate/checks/k8s.js +955 -2
  36. package/dist/gate/checks/mobile-android.js +917 -3
  37. package/dist/gate/checks/mobile-ios.js +797 -5
  38. package/dist/gate/checks/required-artifacts.js +194 -0
  39. package/dist/gate/checks/runtime.js +178 -0
  40. package/dist/gate/checks/secrets.js +256 -13
  41. package/dist/gate/checks/supply-chain-deep.js +787 -0
  42. package/dist/gate/checks/web-nextjs.js +572 -48
  43. package/dist/gate/cloud-controls/apply.js +115 -0
  44. package/dist/gate/cloud-controls/bicep.js +36 -0
  45. package/dist/gate/cloud-controls/cfn.js +125 -0
  46. package/dist/gate/cloud-controls/detect.js +104 -0
  47. package/dist/gate/cloud-controls/hcl.js +140 -0
  48. package/dist/gate/cloud-controls/types.js +87 -0
  49. package/dist/gate/diff.js +17 -5
  50. package/dist/gate/evidence.js +8 -1
  51. package/dist/gate/exceptions.js +202 -9
  52. package/dist/gate/findings.js +15 -2
  53. package/dist/gate/policy.js +316 -130
  54. package/dist/gate/threat-intel.js +6 -0
  55. package/dist/mcp/audit-chain.js +131 -28
  56. package/dist/mcp/auth.js +169 -0
  57. package/dist/mcp/learning.js +129 -4
  58. package/dist/mcp/model-router.js +161 -24
  59. package/dist/mcp/orchestration.js +377 -89
  60. package/dist/mcp/server.js +460 -69
  61. package/dist/mcp/tool-audit.js +193 -0
  62. package/dist/repo/fs.js +37 -1
  63. package/dist/repo/search.js +31 -6
  64. package/dist/review/store.js +56 -3
  65. package/dist/tests/run.js +124 -1
  66. package/package.json +9 -9
  67. package/skills/_TEMPLATE/SKILL.md +99 -0
  68. package/skills/advanced-dos-tester/SKILL.md +118 -0
  69. package/skills/agentic-instruction-auditor/SKILL.md +111 -0
  70. package/skills/agentic-loop-exploiter/SKILL.md +377 -0
  71. package/skills/ai-llm-redteam/SKILL.md +113 -0
  72. package/skills/ai-model-supply-chain-agent/SKILL.md +112 -0
  73. package/skills/algorithm-implementation-reviewer/SKILL.md +107 -0
  74. package/skills/android-penetration-tester/SKILL.md +464 -46
  75. package/skills/anti-replay-tester/SKILL.md +115 -0
  76. package/skills/appsec-code-auditor/SKILL.md +94 -0
  77. package/skills/artifact-integrity-analyst/SKILL.md +450 -0
  78. package/skills/attack-navigator/SKILL.md +476 -8
  79. package/skills/auth-session-hacker/SKILL.md +111 -0
  80. package/skills/aws-penetration-tester/SKILL.md +510 -0
  81. package/skills/azure-penetration-tester/SKILL.md +542 -3
  82. package/skills/binary-auth-validator/SKILL.md +120 -0
  83. package/skills/bot-detection-specialist/SKILL.md +118 -0
  84. package/skills/business-logic-attacker/SKILL.md +240 -0
  85. package/skills/capec-code-mapper/SKILL.md +93 -0
  86. package/skills/cert-pin-rotation-specialist/SKILL.md +121 -0
  87. package/skills/cicd-pipeline-hijacker/SKILL.md +414 -0
  88. package/skills/ciso-orchestrator/SKILL.md +465 -43
  89. package/skills/cloud-infra-specialist/SKILL.md +127 -0
  90. package/skills/compliance-gap-analyst/SKILL.md +431 -0
  91. package/skills/compliance-grc/SKILL.md +94 -0
  92. package/skills/compliance-lifecycle-tracker/SKILL.md +93 -0
  93. package/skills/container-hardening-auditor/SKILL.md +125 -0
  94. package/skills/credential-stuffing-specialist/SKILL.md +111 -0
  95. package/skills/crypto-pki-specialist/SKILL.md +96 -0
  96. package/skills/csa-ccm-mapper/SKILL.md +93 -0
  97. package/skills/csf2-governance-mapper/SKILL.md +93 -0
  98. package/skills/data-platform-auditor/SKILL.md +125 -0
  99. package/skills/deep-link-fuzzer/SKILL.md +118 -0
  100. package/skills/dependency-confusion-attacker/SKILL.md +424 -0
  101. package/skills/device-integrity-aggregator/SKILL.md +117 -0
  102. package/skills/dos-resilience-tester/SKILL.md +106 -0
  103. package/skills/dread-scorer/SKILL.md +93 -0
  104. package/skills/egress-policy-enforcer/SKILL.md +108 -0
  105. package/skills/evidence-collector/SKILL.md +107 -0
  106. package/skills/file-upload-attacker/SKILL.md +118 -0
  107. package/skills/gcp-penetration-tester/SKILL.md +510 -2
  108. package/skills/git-history-secret-scanner/SKILL.md +115 -0
  109. package/skills/gitops-delivery-auditor/SKILL.md +120 -0
  110. package/skills/iac-security-auditor/SKILL.md +125 -0
  111. package/skills/iam-privesc-graph-builder/SKILL.md +161 -0
  112. package/skills/incident-responder/SKILL.md +120 -0
  113. package/skills/injection-specialist/SKILL.md +111 -0
  114. package/skills/ios-security-auditor/SKILL.md +291 -0
  115. package/skills/json-ambiguity-tester/SKILL.md +145 -0
  116. package/skills/k8s-container-escaper/SKILL.md +406 -0
  117. package/skills/key-management-lifecycle-analyst/SKILL.md +107 -0
  118. package/skills/kill-switch-engineer/SKILL.md +111 -0
  119. package/skills/linddun-privacy-analyst/SKILL.md +111 -0
  120. package/skills/logic-race-fuzzer/SKILL.md +452 -0
  121. package/skills/mobile-api-network-attacker/SKILL.md +430 -0
  122. package/skills/mobile-binary-hardener/SKILL.md +111 -0
  123. package/skills/mobile-security-specialist/SKILL.md +94 -0
  124. package/skills/mobile-webview-auditor/SKILL.md +105 -0
  125. package/skills/model-extraction-attacker/SKILL.md +228 -0
  126. package/skills/multipart-abuse-tester/SKILL.md +93 -0
  127. package/skills/oauth-pkce-specialist/SKILL.md +113 -0
  128. package/skills/parser-exhaustion-tester/SKILL.md +151 -0
  129. package/skills/pentest-infra/SKILL.md +107 -0
  130. package/skills/pentest-social/SKILL.md +210 -0
  131. package/skills/pentest-team/SKILL.md +96 -0
  132. package/skills/pentest-web-api/SKILL.md +107 -0
  133. package/skills/privacy-flow-analyst/SKILL.md +243 -0
  134. package/skills/prompt-injection-specialist/SKILL.md +403 -0
  135. package/skills/quantum-migration-planner/SKILL.md +105 -0
  136. package/skills/rag-poisoning-specialist/SKILL.md +367 -0
  137. package/skills/registry-mirror-enforcer/SKILL.md +93 -0
  138. package/skills/rotation-validation-agent/SKILL.md +121 -0
  139. package/skills/samm-assessor/SKILL.md +94 -0
  140. package/skills/secrets-mask-bypass-tester/SKILL.md +109 -0
  141. package/skills/senior-security-engineer/SKILL.md +178 -0
  142. package/skills/serialization-memory-attacker/SKILL.md +341 -0
  143. package/skills/session-timeout-tester/SKILL.md +170 -0
  144. package/skills/slsa-level3-enforcer/SKILL.md +121 -0
  145. package/skills/slsa-provenance-enforcer/SKILL.md +111 -0
  146. package/skills/ssrf-detection-validator/SKILL.md +117 -0
  147. package/skills/step-up-auth-enforcer/SKILL.md +93 -0
  148. package/skills/stride-pasta-analyst/SKILL.md +429 -0
  149. package/skills/supply-chain-devsecops/SKILL.md +107 -0
  150. package/skills/threat-infrastructure-analyst/SKILL.md +93 -0
  151. package/skills/threat-modeler/SKILL.md +94 -0
  152. package/skills/tls-certificate-auditor/SKILL.md +582 -18
  153. package/skills/token-reuse-detector/SKILL.md +104 -0
  154. package/skills/trike-risk-modeler/SKILL.md +93 -0
  155. package/skills/unicode-homograph-tester/SKILL.md +93 -0
  156. package/skills/waf-rule-lifecycle-agent/SKILL.md +106 -0
  157. package/skills/webhook-security-tester/SKILL.md +111 -0
  158. package/skills/zero-trust-architect/SKILL.md +118 -0
@@ -6,6 +6,191 @@ import { sanitizeErrorMessage } from "../result.js";
6
6
  import { searchRepo } from "../../repo/search.js";
7
7
  import fg from "fast-glob";
8
8
  import { readFileSafe } from "../../repo/fs.js";
9
+ async function checkGraphqlIntrospection() {
10
+ const findings = [];
11
+ // Find GraphQL server instantiation sites
12
+ const serverHits = await searchRepo({
13
+ query: String.raw `ApolloServer|createServer|buildSchema|makeExecutableSchema|new GraphQL`,
14
+ isRegex: true,
15
+ maxMatches: 200
16
+ });
17
+ if (serverHits.length === 0)
18
+ return findings;
19
+ // Check for explicit disabling of introspection near server setup
20
+ const disableHits = await searchRepo({
21
+ query: String.raw `introspection\s*:\s*false|disableIntrospection|NoIntrospection|validationRules.*introspection`,
22
+ isRegex: true,
23
+ maxMatches: 200
24
+ });
25
+ // Check for introspection: true explicitly set
26
+ const alwaysOnHits = await searchRepo({
27
+ query: String.raw `introspection\s*:\s*true`,
28
+ isRegex: true,
29
+ maxMatches: 200
30
+ });
31
+ // Filter always-on hits that have no NODE_ENV guard
32
+ const unguardedAlwaysOn = alwaysOnHits.filter((m) => !/NODE_ENV|process\.env/i.test(m.preview));
33
+ if (unguardedAlwaysOn.length > 0) {
34
+ findings.push({
35
+ id: "GRAPHQL_INTROSPECTION_ALWAYS_ON",
36
+ title: "GraphQL introspection is explicitly enabled without a NODE_ENV guard",
37
+ severity: "CRITICAL",
38
+ evidence: unguardedAlwaysOn.slice(0, 10).map((m) => `${m.file}:${m.line}:${m.preview}`),
39
+ files: [...new Set(unguardedAlwaysOn.slice(0, 10).map((m) => m.file))],
40
+ requiredActions: [
41
+ "Disable introspection unconditionally in production.",
42
+ "Use `introspection: process.env.NODE_ENV !== 'production'` at minimum."
43
+ ]
44
+ });
45
+ }
46
+ else if (disableHits.length === 0) {
47
+ // Server setup found but introspection is not explicitly disabled
48
+ findings.push({
49
+ id: "GRAPHQL_INTROSPECTION_ENABLED",
50
+ title: "GraphQL introspection is enabled by default; ensure it is disabled in production with `introspection: process.env.NODE_ENV !== 'production'`",
51
+ severity: "HIGH",
52
+ evidence: serverHits.slice(0, 10).map((m) => `${m.file}:${m.line}:${m.preview}`),
53
+ files: [...new Set(serverHits.slice(0, 10).map((m) => m.file))],
54
+ requiredActions: [
55
+ "Disable introspection in non-dev environments.",
56
+ "Use persisted queries instead of ad-hoc introspection in production."
57
+ ]
58
+ });
59
+ }
60
+ return findings;
61
+ }
62
+ async function checkGraphqlAliasAmplification(graphqlInUse) {
63
+ if (!graphqlInUse)
64
+ return [];
65
+ const findings = [];
66
+ const complexityHits = await searchRepo({
67
+ query: String.raw `complexityPlugin|costAnalysis|queryComplexity|createComplexityRule`,
68
+ isRegex: true,
69
+ maxMatches: 200
70
+ });
71
+ if (complexityHits.length === 0) {
72
+ findings.push({
73
+ id: "GRAPHQL_NO_COMPLEXITY_LIMIT",
74
+ title: "No GraphQL query complexity limiter detected",
75
+ severity: "HIGH",
76
+ requiredActions: [
77
+ "Add graphql-query-complexity or graphql-cost-analysis to limit query cost.",
78
+ "Set a maximum complexity budget to prevent amplified alias abuse."
79
+ ]
80
+ });
81
+ return findings;
82
+ }
83
+ // Complexity limiter found — check if it accounts for aliases
84
+ const aliasHits = await searchRepo({
85
+ query: String.raw `aliasCost|aliasMultiplier|alias.*cost|fieldCost.*alias`,
86
+ isRegex: true,
87
+ maxMatches: 200
88
+ });
89
+ if (aliasHits.length === 0) {
90
+ findings.push({
91
+ id: "GRAPHQL_ALIAS_AMPLIFICATION",
92
+ title: "GraphQL complexity limiter found but alias cost not configured — alias amplification attacks possible",
93
+ severity: "HIGH",
94
+ evidence: complexityHits.slice(0, 10).map((m) => `${m.file}:${m.line}:${m.preview}`),
95
+ files: [...new Set(complexityHits.slice(0, 10).map((m) => m.file))],
96
+ requiredActions: [
97
+ "Configure alias cost or alias multiplier in the complexity plugin.",
98
+ "Without alias accounting, attackers can use field aliasing to bypass complexity limits."
99
+ ]
100
+ });
101
+ }
102
+ return findings;
103
+ }
104
+ async function checkGraphqlResolverInjection() {
105
+ const findings = [];
106
+ const resolverInjectionHits = await searchRepo({
107
+ query: String.raw `(?:resolve|resolver)\s*\([^)]*\)\s*\{[^}]*(?:SELECT|INSERT|UPDATE|DELETE|\$where|\$regex|aggregate)\s*['"].*\$\{args\.`,
108
+ isRegex: true,
109
+ maxMatches: 200
110
+ });
111
+ const resolverInjectionBroadHits = await searchRepo({
112
+ query: String.raw `resolve.*\{[^}]*(?:SELECT|INSERT)[^}]*\$\{args`,
113
+ isRegex: true,
114
+ maxMatches: 200
115
+ });
116
+ const allHits = [...resolverInjectionHits, ...resolverInjectionBroadHits];
117
+ const uniqueHits = allHits.filter((hit, idx, arr) => arr.findIndex((h) => h.file === hit.file && h.line === hit.line) === idx);
118
+ if (uniqueHits.length > 0) {
119
+ findings.push({
120
+ id: "GRAPHQL_RESOLVER_INJECTION",
121
+ title: "GraphQL resolver argument concatenated into raw SQL/NoSQL query — injection via resolver args (CWE-89/CWE-943)",
122
+ severity: "CRITICAL",
123
+ evidence: uniqueHits.slice(0, 10).map((m) => `${m.file}:${m.line}:${m.preview}`),
124
+ files: [...new Set(uniqueHits.slice(0, 10).map((m) => m.file))],
125
+ requiredActions: [
126
+ "Never interpolate resolver args directly into SQL or NoSQL queries.",
127
+ "Use parameterized queries or an ORM to pass resolver arguments safely.",
128
+ "Validate and sanitize all args before use in any query expression."
129
+ ]
130
+ });
131
+ }
132
+ return findings;
133
+ }
134
+ async function checkGraphqlAliasBatching() {
135
+ const findings = [];
136
+ const serverHits = await searchRepo({
137
+ query: String.raw `(?:ApolloServer|makeExecutableSchema|buildSchema|graphqlHTTP)\s*\(`,
138
+ isRegex: true,
139
+ maxMatches: 200
140
+ });
141
+ if (serverHits.length === 0)
142
+ return findings;
143
+ const aliasLimitHits = await searchRepo({
144
+ query: String.raw `(?:maxAliasCount|depthLimit|complexityLimit|queryComplexity|fieldExtensions.*complexity)`,
145
+ isRegex: true,
146
+ maxMatches: 200
147
+ });
148
+ if (aliasLimitHits.length === 0) {
149
+ findings.push({
150
+ id: "GRAPHQL_ALIAS_BATCHING",
151
+ title: "GraphQL server without alias count limit — N+1 batching enables account enumeration and DoS (CWE-770)",
152
+ severity: "HIGH",
153
+ evidence: serverHits.slice(0, 10).map((m) => `${m.file}:${m.line}:${m.preview}`),
154
+ files: [...new Set(serverHits.slice(0, 10).map((m) => m.file))],
155
+ requiredActions: [
156
+ "Add a maxAliasCount or equivalent alias limit to the GraphQL server configuration.",
157
+ "Use graphql-query-complexity or graphql-depth-limit to bound alias expansion.",
158
+ "Without limits, attackers can batch aliased fields to enumerate data or exhaust backend resources."
159
+ ]
160
+ });
161
+ }
162
+ return findings;
163
+ }
164
+ async function checkGraphqlCircularFragments(graphqlInUse) {
165
+ if (!graphqlInUse)
166
+ return [];
167
+ const findings = [];
168
+ const fragmentProtectionHits = await searchRepo({
169
+ query: String.raw `NoSchemaIntrospectionCustomRule|maxFragmentDepth|FragmentDepthLimit`,
170
+ isRegex: true,
171
+ maxMatches: 200
172
+ });
173
+ const validationRulesHits = await searchRepo({
174
+ query: String.raw `specifiedRules|validationRules`,
175
+ isRegex: true,
176
+ maxMatches: 200
177
+ });
178
+ const hasFragmentProtection = fragmentProtectionHits.length > 0 ||
179
+ validationRulesHits.some((m) => /maxFragmentDepth|FragmentDepthLimit/i.test(m.preview));
180
+ if (!hasFragmentProtection) {
181
+ findings.push({
182
+ id: "GRAPHQL_CIRCULAR_FRAGMENT_RISK",
183
+ title: "No GraphQL fragment depth limiting detected — circular fragment DoS risk",
184
+ severity: "MEDIUM",
185
+ requiredActions: [
186
+ "Add fragment depth limiting via a custom validation rule.",
187
+ "Use graphql-depth-limit or implement NoSchemaIntrospectionCustomRule with fragment cycle detection.",
188
+ "Circular fragments can be used to exhaust server resources."
189
+ ]
190
+ });
191
+ }
192
+ return findings;
193
+ }
9
194
  export async function checkGraphQL(_opts) {
10
195
  const findings = [];
11
196
  try {
@@ -18,25 +203,10 @@ export async function checkGraphQL(_opts) {
18
203
  if (graphqlHits.length === 0) {
19
204
  return [];
20
205
  }
21
- // 2. Introspection enabled in prod
22
- const introspectionHits = await searchRepo({
23
- query: String.raw `introspection.*true|disableIntrospection.*false|GraphQLSchema.*introspection`,
24
- isRegex: true,
25
- maxMatches: 200
26
- });
27
- if (introspectionHits.length > 0) {
28
- findings.push({
29
- id: "GRAPHQL_INTROSPECTION_ENABLED",
30
- title: "GraphQL introspection is enabled — exposes full schema to attackers",
31
- severity: "HIGH",
32
- evidence: introspectionHits.slice(0, 10).map((m) => `${m.file}:${m.line}:${m.preview}`),
33
- files: [...new Set(introspectionHits.slice(0, 10).map((m) => m.file))],
34
- requiredActions: [
35
- "Disable introspection in non-dev environments.",
36
- "Use persisted queries instead of ad-hoc introspection in production."
37
- ]
38
- });
39
- }
206
+ const graphqlInUse = true;
207
+ // 2. Introspection check (corrected: fire when NOT explicitly disabled)
208
+ const introspectionFindings = await checkGraphqlIntrospection();
209
+ findings.push(...introspectionFindings);
40
210
  // 3. No query depth/complexity limiting
41
211
  const depthLimitHits = await searchRepo({
42
212
  query: String.raw `depthLimit|complexityLimit|queryComplexity|createComplexityRule|maxDepth`,
@@ -119,6 +289,18 @@ export async function checkGraphQL(_opts) {
119
289
  ]
120
290
  });
121
291
  }
292
+ // 7. Alias amplification detection
293
+ const aliasFindings = await checkGraphqlAliasAmplification(graphqlInUse);
294
+ findings.push(...aliasFindings);
295
+ // 8. Circular fragment protection
296
+ const fragmentFindings = await checkGraphqlCircularFragments(graphqlInUse);
297
+ findings.push(...fragmentFindings);
298
+ // 9. Resolver injection
299
+ const resolverInjectionFindings = await checkGraphqlResolverInjection();
300
+ findings.push(...resolverInjectionFindings);
301
+ // 10. Alias batching without limit
302
+ const aliasBatchingFindings = await checkGraphqlAliasBatching();
303
+ findings.push(...aliasBatchingFindings);
122
304
  }
123
305
  catch (err) {
124
306
  console.warn("[checkGraphQL] Internal error:", sanitizeErrorMessage(err instanceof Error ? err.message : String(err)));