sast-graphql-be5e32 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited sast-graphql-be5e32 (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are performing a focused security assessment to find GraphQL injection vulnerabilities. This skill uses a three-phase approach with subagents: recon (confirm GraphQL usage and find every location where a GraphQL operation document is assembled unsafely), batched verify (trace whether user-supplied input reaches those assembly sites, in parallel batches of up to 3 sites each), and merge (consolidate batch results into the final report).
Prerequisites: sast/architecture.md must exist. Run the analysis skill first if it doesn't.
GraphQL injection occurs when user-controlled data is embedded into the GraphQL document (the query, mutation, or subscription string) rather than passed only through the variables map. The parser then interprets attacker-controlled syntax — new fields, aliases, directives, or fragments — which can bypass intent, reach unauthorized resolvers, or change server-side behavior when that document is executed or forwarded.
The core pattern: unvalidated user input alters the structure or text of the GraphQL operation string passed to `execute`, `graphql`, a gateway client, or an HTTP body `query` field built from string operations.
query { user(id: "${id}") { name } } , "query { user(id: \"" + id + "\") { name } }"`query field for a downstream GraphQL HTTP request with string concat from request body or paramsreq.body.query (or similar) into another interpolated template that wraps or extends the operationgql / graphql-tag template literals where a non-static expression changes document structure (not just a bound variable value inside a static document)graphql.execute(), graphqlHTTP, Yoga/Apollo request pipeline where the first argument (document/source) is built from variables that could be user-influencedDo not flag these as GraphQL injection:
args — that is SQL injection (sast-sqli), not this skill{"query": "query($id: ID!) { user(id: $id) { name } }", "variables": {"id": userInput}} — values are bound as variables; the document structure is fixed (still verify authorization in resolvers)1. Static operation documents with variables
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) { name }
}
`;
// execute(schema, GET_USER, null, context, { id: userId });2. Server uses standard HTTP handler; client sends document; server parses once
The risk is not the mere presence of req.body.query on the server if the server only parses and executes it as the client's operation — injection in that path is client-side. Flag server-side construction of a new document that incorporates user strings before execute or before forwarding.
3. Persisted queries / allowlisted operation IDs
Document looked up by ID from a server-side registry; client cannot inject arbitrary document text.
4. graphql-js `Source` with static string; dynamic values only in variableValues
graphql({ schema, source: staticQueryString, variableValues: { id: userId } });// VULNERABLE: user input in operation text
app.post('/proxy', async (req, res) => {
const fragment = req.body.fragment;
const query = `query { me { ${fragment} } }`;
const data = await fetch('https://api.internal/graphql', {
method: 'POST',
body: JSON.stringify({ query }),
});
});
// SECURE: static operation, user data only in variables
const PROXY_QUERY = `query ProxyMe { me { id name email } }`;
app.post('/proxy', async (req, res) => {
const data = await fetch('https://api.internal/graphql', {
method: 'POST',
body: JSON.stringify({ query: PROXY_QUERY }),
});
});# VULNERABLE
def run_custom_query(user_gql: str):
document = f"query {{ user {{ {user_gql} }} }}"
return graphql_sync(schema, document)
# SECURE: validate against allowlist of named operations or use static documents only
ALLOWED = {"id", "name", "email"}
fields = [f for f in requested_fields if f in ALLOWED]
document = "query { user { " + " ".join(ALLOWED.intersection(set(requested_fields))) + " } }"
# Better: fixed FieldNodes, not string building from user inputThis skill runs in three phases using subagents. Pass the contents of sast/architecture.md to all subagents as context.
Launch a subagent with the following instructions:
Goal: (1) Determine whether this codebase uses GraphQL at all. (2) If it does, find every location where a GraphQL operation document (query/mutation/subscription source string) is built using string concatenation, interpolation, formatting, or dynamic assembly such that a variable could change the document text (not merelyvariablesJSON). Write results tosast/graphql-recon.md.
>
Context: You will be given the project's architecture summary. Use it for stack, API layout, and BFF/gateway patterns.
>
Part A — Is GraphQL used?
>
Search for: - Dependencies:graphql,@apollo/server,apollo-server-express,@nestjs/graphql,graphql-yoga,@graphql-yoga/node,mercurius,strawberry-graphql,graphene,sangria,gqlgen,async-graphql,juniper,graphql-ruby, Hot Chocolate /GraphQL.Server, etc. - Schema artifacts:*.graphql,*.graphqls, codegen config (e.g. GraphQL Code Generator) - Server routes or plugins mounting/graphqlor similar
>
Set the summary to exactly one of: -GraphQL is used in this codebase.(list libraries and main entry points) -GraphQL is not used in this codebase.
>
Part B — Injection candidate sites (only if GraphQL is used)
>
If GraphQL is not used, omit the "Injection Candidate Sites" section or state there are none. Do not invent candidates.
>
If GraphQL is used, search for unsafe document construction:
>
1. String concatenation / interpolation into operation text: - `query { ... ${x} ...},"mutation { " + userFragment + " }"-sprintf,format,%formatting,.format()buildingqueryorsource` arguments
>
2. Calls where the document argument is not a compile-time constant: -graphql(schema, dynamicString, ...),execute({ schema, document: parsedDynamic, ...})where the string feedingparseorexecuteis built from non-static parts -graphqlHTTP({ schema, rootValue, context: (req) => ({ query: req.body.query + something }) })patterns that mutate or wrap the query string with user data
>
3. HTTP clients forwarding a constructed GraphQL body: -JSON.stringify({ query:...${userPart}...}),axios.post(url, { query: builtFromInput })
>
4. Unsafe persisted / stored query lookup: - Operation text loaded by key from user input without allowlist → file path or DB value becomes document source
>
What to skip (do not flag as Phase 1 candidates): - Fully staticsource/querystrings; onlyvariableValues/variablescome from the request - Schema definition withbuildSchema/ SDL files with no user interpolation - Resolver implementations that only use args with parameterized DB APIs (optional: note "resolver uses ORM" but not a GraphQL injection candidate unless the document is built unsafely)
>
Output format — write to sast/graphql-recon.md:>
```markdown # GraphQL Recon: [Project Name]
>
## Summary GraphQL is [used / not used] in this codebase. [If used: libraries, main server files, typical endpoint paths] Found [N] injection candidate site(s) where operation documents may be built unsafely. [If not used, say N/A or 0 and skip candidate list]
>
## GraphQL Surface (only if used) - Libraries / frameworks: ... - Entry points: ... - Notable files: ...
>
## Injection Candidate Sites
>
### 1. [Descriptive name] - File:path/to/file.ext(lines X-Y) - Function / endpoint: ... - Execution / call pattern: [graphql.execute / fetch with body / gql template / etc.] - Construction pattern: [concat / template literal / format / forwarded body mutation] - Interpolated variable(s): ... - Code snippet: ``...``
>
[Repeat for each site; if none, write "No injection candidate sites found." under the heading] ```
After Phase 1 completes, read sast/graphql-recon.md.
Gate 1 — No GraphQL technology
If the summary states GraphQL is not used (or equivalent: no GraphQL libraries, no schema, no server — clear absence), skip Phases 2 and 3. Write the following to sast/graphql-results.md and stop:
# GraphQL Injection Analysis Results
No GraphQL technology detected in this codebase.Gate 2 — GraphQL used but no injection candidates
If GraphQL is used but there are zero injection candidate sites (summary reports 0 candidates, or the "Injection Candidate Sites" section states none found / is empty), skip Phases 2 and 3. Write the following to sast/graphql-results.md and stop:
# GraphQL Injection Analysis Results
No vulnerabilities found.Otherwise proceed to Phase 2.
After Phase 1 completes and both gates pass (GraphQL used and at least one candidate site), read sast/graphql-recon.md and split the Injection Candidate Sites into batches of up to 3 sites each (each ### N. section is one site). Launch one subagent per batch in parallel. Each subagent traces taint only for its assigned sites and writes results to its own batch file.
Batching procedure (you, the orchestrator, do this — not a subagent):
sast/graphql-recon.md and count the numbered candidate sections under "Injection Candidate Sites" (### 1., ### 2., etc.).sast/graphql-batch-N.md where N is the 1-based batch number.sast/architecture.md and select only the matching examples from the "Vulnerable vs. Secure Examples" section above. For example, if the project uses Node.js, include the "Node.js — dynamic document for downstream API" example; if Python, include "Python — string format into execute". Include these selected examples in each subagent's instructions where indicated by [TECH-STACK EXAMPLES] below.Give each batch subagent the following instructions (substitute the batch-specific values):
Goal: For each assigned injection candidate site, determine whether user-supplied data can reach the dynamic part of the operation document. Our goal is to find GraphQL injection vulnerabilities. Write results to sast/graphql-batch-[N].md.>
Your assigned candidate sites (from the recon phase):
>
[Paste the full text of the assigned candidate sections here, preserving the original numbering]
>
Context: You will be given the project's architecture summary. Use it for API layout, request handling, and BFF/gateway patterns.
>
GraphQL injection reference — What to look for:
>
User-controlled data must not alter the GraphQL document text (query/mutation/subscription source) except through bound variables on a static document. Flag when taint reaches string assembly of the operation.
>
What GraphQL injection is NOT — do not flag these here: - SQL/NoSQL injection in resolvers — other SAST skills - IDOR with static document + variables — authorization, not document injection - Normal variable binding on a fixed document string - Introspection enabled — unless the finding is specifically operation-string injection - Query depth/complexity DoS — different class
>
Mitigations that reduce risk: - Allowlist of fields or operation IDs before any string assembly - Parser validation that rejects unexpected definitions (still prefer no user-controlled document structure)
>
Vulnerable vs. secure examples for this project's tech stack:
>
[TECH-STACK EXAMPLES]
>
For each assigned site, trace dynamic values backward:
>
1. Direct user input — query params, path params, JSON body fields (including nested query if re-wrapped), headers, cookies 2. Indirect user input — helpers, middleware, context builders 3. Second-order — stored preferences or DB fields later used to build a document; trace write path 4. Server-only — config, env, hardcoded fragments — not exploitable from the client>
Classification: - Vulnerable: User-controlled data reaches document construction with no effective mitigation - Likely Vulnerable: Probable taint or weak sanitization - Not Vulnerable: Server-side-only or effective allowlist / static document path - Needs Manual Review: Opaque flow
>
Output format — write to sast/graphql-batch-[N].md:>
```markdown # GraphQL Batch [N] Results
>
## Findings
>
### [VULNERABLE] Descriptive name - File:path/to/file.ext(lines X-Y) - Endpoint / function: ... - Issue: ... - Taint trace: ... - Impact: [e.g., unauthorized fields, gateway bypass, SSRF-style behavior to internal GraphQL] - Remediation: [static operations; variables only; persisted query allowlist] - Dynamic Test: ``[curl or in-browser GraphQL request showing injected fragment/directive/field]``
>
### [LIKELY VULNERABLE] Descriptive name - File: ... - Endpoint / function: ... - Issue: ... - Taint trace: ... - Concern: ... - Remediation: ... - Dynamic Test: `` ... ``>
### [NOT VULNERABLE] Descriptive name - File: ... - Endpoint / function: ... - Reason: ...
>
### [NEEDS MANUAL REVIEW] Descriptive name - File: ... - Endpoint / function: ... - Uncertainty: ... - Suggestion: ... ```
After all Phase 2 batch subagents complete, read every sast/graphql-batch-*.md file and merge them into a single sast/graphql-results.md. You (the orchestrator) do this directly — no subagent needed.
Merge procedure:
sast/graphql-batch-1.md, sast/graphql-batch-2.md, ... files.sast/graphql-results.md using this format:# GraphQL Injection Analysis Results: [Project Name]
## Executive Summary
- Candidate sites analyzed: [total across all batches]
- Vulnerable: [N]
- Likely Vulnerable: [N]
- Not Vulnerable: [N]
- Needs Manual Review: [N]
## Findings
[All findings from all batches, grouped by classification:
VULNERABLE first, then LIKELY VULNERABLE, then NEEDS MANUAL REVIEW, then NOT VULNERABLE.
Preserve every field from the batch results exactly as written.]sast/graphql-results.md, delete all intermediate batch files (sast/graphql-batch-*.md).sast/architecture.md and pass its content to all subagents as context.sast/graphql-recon.md and all sast/graphql-batch-*.md files after the final sast/graphql-results.md is written.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.