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.
- package/README.md +341 -1018
- package/defaults/checklists/ai.json +20 -1
- package/defaults/checklists/api.json +35 -1
- package/defaults/checklists/infra.json +34 -1
- package/defaults/checklists/mobile.json +23 -1
- package/defaults/checklists/payments.json +15 -1
- package/defaults/checklists/web.json +11 -1
- package/defaults/cloud-controls/aws.json +10712 -0
- package/defaults/cloud-controls/azure.json +7201 -0
- package/defaults/cloud-controls/gcp.json +4061 -0
- package/defaults/control-catalog.json +24 -0
- package/defaults/security-policy.json +2 -2
- package/dist/ci/pr-gate.js +22 -5
- package/dist/cli/index.js +73 -2
- package/dist/cli/install.js +4 -55
- package/dist/cli/onboarding.js +18 -10
- package/dist/gate/baseline.js +82 -7
- package/dist/gate/catalog.js +10 -2
- package/dist/gate/checks/agentic-instructions.js +515 -0
- package/dist/gate/checks/ai-governance.js +132 -0
- package/dist/gate/checks/ai.js +757 -39
- package/dist/gate/checks/auth-deep.js +920 -216
- package/dist/gate/checks/business-logic.js +751 -0
- package/dist/gate/checks/ci-pipeline.js +399 -4
- package/dist/gate/checks/cloud-controls.js +69 -0
- package/dist/gate/checks/crypto.js +423 -2
- package/dist/gate/checks/data-platform.js +954 -0
- package/dist/gate/checks/dependencies.js +582 -15
- package/dist/gate/checks/docker-deep.js +1236 -0
- package/dist/gate/checks/gitops.js +724 -0
- package/dist/gate/checks/graphql.js +201 -19
- package/dist/gate/checks/iac.js +1230 -0
- package/dist/gate/checks/infra.js +246 -1
- package/dist/gate/checks/injection-deep.js +827 -184
- package/dist/gate/checks/k8s.js +955 -2
- package/dist/gate/checks/mobile-android.js +917 -3
- package/dist/gate/checks/mobile-ios.js +797 -5
- package/dist/gate/checks/required-artifacts.js +194 -0
- package/dist/gate/checks/runtime.js +178 -0
- package/dist/gate/checks/secrets.js +256 -13
- package/dist/gate/checks/supply-chain-deep.js +787 -0
- package/dist/gate/checks/web-nextjs.js +572 -48
- package/dist/gate/cloud-controls/apply.js +115 -0
- package/dist/gate/cloud-controls/bicep.js +36 -0
- package/dist/gate/cloud-controls/cfn.js +125 -0
- package/dist/gate/cloud-controls/detect.js +104 -0
- package/dist/gate/cloud-controls/hcl.js +140 -0
- package/dist/gate/cloud-controls/types.js +87 -0
- package/dist/gate/diff.js +17 -5
- package/dist/gate/evidence.js +8 -1
- package/dist/gate/exceptions.js +202 -9
- package/dist/gate/findings.js +15 -2
- package/dist/gate/policy.js +316 -130
- package/dist/gate/threat-intel.js +6 -0
- package/dist/mcp/audit-chain.js +131 -28
- package/dist/mcp/auth.js +169 -0
- package/dist/mcp/learning.js +129 -4
- package/dist/mcp/model-router.js +161 -24
- package/dist/mcp/orchestration.js +377 -89
- package/dist/mcp/server.js +460 -69
- package/dist/mcp/tool-audit.js +193 -0
- package/dist/repo/fs.js +37 -1
- package/dist/repo/search.js +31 -6
- package/dist/review/store.js +56 -3
- package/dist/tests/run.js +124 -1
- package/package.json +9 -9
- package/skills/_TEMPLATE/SKILL.md +99 -0
- package/skills/advanced-dos-tester/SKILL.md +118 -0
- package/skills/agentic-instruction-auditor/SKILL.md +111 -0
- package/skills/agentic-loop-exploiter/SKILL.md +377 -0
- package/skills/ai-llm-redteam/SKILL.md +113 -0
- package/skills/ai-model-supply-chain-agent/SKILL.md +112 -0
- package/skills/algorithm-implementation-reviewer/SKILL.md +107 -0
- package/skills/android-penetration-tester/SKILL.md +464 -46
- package/skills/anti-replay-tester/SKILL.md +115 -0
- package/skills/appsec-code-auditor/SKILL.md +94 -0
- package/skills/artifact-integrity-analyst/SKILL.md +450 -0
- package/skills/attack-navigator/SKILL.md +476 -8
- package/skills/auth-session-hacker/SKILL.md +111 -0
- package/skills/aws-penetration-tester/SKILL.md +510 -0
- package/skills/azure-penetration-tester/SKILL.md +542 -3
- package/skills/binary-auth-validator/SKILL.md +120 -0
- package/skills/bot-detection-specialist/SKILL.md +118 -0
- package/skills/business-logic-attacker/SKILL.md +240 -0
- package/skills/capec-code-mapper/SKILL.md +93 -0
- package/skills/cert-pin-rotation-specialist/SKILL.md +121 -0
- package/skills/cicd-pipeline-hijacker/SKILL.md +414 -0
- package/skills/ciso-orchestrator/SKILL.md +465 -43
- package/skills/cloud-infra-specialist/SKILL.md +127 -0
- package/skills/compliance-gap-analyst/SKILL.md +431 -0
- package/skills/compliance-grc/SKILL.md +94 -0
- package/skills/compliance-lifecycle-tracker/SKILL.md +93 -0
- package/skills/container-hardening-auditor/SKILL.md +125 -0
- package/skills/credential-stuffing-specialist/SKILL.md +111 -0
- package/skills/crypto-pki-specialist/SKILL.md +96 -0
- package/skills/csa-ccm-mapper/SKILL.md +93 -0
- package/skills/csf2-governance-mapper/SKILL.md +93 -0
- package/skills/data-platform-auditor/SKILL.md +125 -0
- package/skills/deep-link-fuzzer/SKILL.md +118 -0
- package/skills/dependency-confusion-attacker/SKILL.md +424 -0
- package/skills/device-integrity-aggregator/SKILL.md +117 -0
- package/skills/dos-resilience-tester/SKILL.md +106 -0
- package/skills/dread-scorer/SKILL.md +93 -0
- package/skills/egress-policy-enforcer/SKILL.md +108 -0
- package/skills/evidence-collector/SKILL.md +107 -0
- package/skills/file-upload-attacker/SKILL.md +118 -0
- package/skills/gcp-penetration-tester/SKILL.md +510 -2
- package/skills/git-history-secret-scanner/SKILL.md +115 -0
- package/skills/gitops-delivery-auditor/SKILL.md +120 -0
- package/skills/iac-security-auditor/SKILL.md +125 -0
- package/skills/iam-privesc-graph-builder/SKILL.md +161 -0
- package/skills/incident-responder/SKILL.md +120 -0
- package/skills/injection-specialist/SKILL.md +111 -0
- package/skills/ios-security-auditor/SKILL.md +291 -0
- package/skills/json-ambiguity-tester/SKILL.md +145 -0
- package/skills/k8s-container-escaper/SKILL.md +406 -0
- package/skills/key-management-lifecycle-analyst/SKILL.md +107 -0
- package/skills/kill-switch-engineer/SKILL.md +111 -0
- package/skills/linddun-privacy-analyst/SKILL.md +111 -0
- package/skills/logic-race-fuzzer/SKILL.md +452 -0
- package/skills/mobile-api-network-attacker/SKILL.md +430 -0
- package/skills/mobile-binary-hardener/SKILL.md +111 -0
- package/skills/mobile-security-specialist/SKILL.md +94 -0
- package/skills/mobile-webview-auditor/SKILL.md +105 -0
- package/skills/model-extraction-attacker/SKILL.md +228 -0
- package/skills/multipart-abuse-tester/SKILL.md +93 -0
- package/skills/oauth-pkce-specialist/SKILL.md +113 -0
- package/skills/parser-exhaustion-tester/SKILL.md +151 -0
- package/skills/pentest-infra/SKILL.md +107 -0
- package/skills/pentest-social/SKILL.md +210 -0
- package/skills/pentest-team/SKILL.md +96 -0
- package/skills/pentest-web-api/SKILL.md +107 -0
- package/skills/privacy-flow-analyst/SKILL.md +243 -0
- package/skills/prompt-injection-specialist/SKILL.md +403 -0
- package/skills/quantum-migration-planner/SKILL.md +105 -0
- package/skills/rag-poisoning-specialist/SKILL.md +367 -0
- package/skills/registry-mirror-enforcer/SKILL.md +93 -0
- package/skills/rotation-validation-agent/SKILL.md +121 -0
- package/skills/samm-assessor/SKILL.md +94 -0
- package/skills/secrets-mask-bypass-tester/SKILL.md +109 -0
- package/skills/senior-security-engineer/SKILL.md +178 -0
- package/skills/serialization-memory-attacker/SKILL.md +341 -0
- package/skills/session-timeout-tester/SKILL.md +170 -0
- package/skills/slsa-level3-enforcer/SKILL.md +121 -0
- package/skills/slsa-provenance-enforcer/SKILL.md +111 -0
- package/skills/ssrf-detection-validator/SKILL.md +117 -0
- package/skills/step-up-auth-enforcer/SKILL.md +93 -0
- package/skills/stride-pasta-analyst/SKILL.md +429 -0
- package/skills/supply-chain-devsecops/SKILL.md +107 -0
- package/skills/threat-infrastructure-analyst/SKILL.md +93 -0
- package/skills/threat-modeler/SKILL.md +94 -0
- package/skills/tls-certificate-auditor/SKILL.md +582 -18
- package/skills/token-reuse-detector/SKILL.md +104 -0
- package/skills/trike-risk-modeler/SKILL.md +93 -0
- package/skills/unicode-homograph-tester/SKILL.md +93 -0
- package/skills/waf-rule-lifecycle-agent/SKILL.md +106 -0
- package/skills/webhook-security-tester/SKILL.md +111 -0
- 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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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)));
|