GraphQL injection, introspection abuse, query complexity attacks, and authorization bypass detection
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill sc-graphql --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Sc Graphql?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-sc-graphql)More formats (shields.io, HTML) on the badges page.
---
name: sc-graphql
description: GraphQL injection, introspection abuse, query complexity attacks, and authorization bypass detection
license: MIT
metadata:
category: security
version: "1.0.0"
---
# SC: GraphQL Security
## Purpose
Detects GraphQL-specific security vulnerabilities including query injection, introspection information disclosure, batching abuse, nested query denial-of-service, field-level authorization bypass, and subscription hijacking. Covers Apollo Server, graphql-yoga, Strawberry, Graphene, gqlgen, and HotChocolate.
## Activation
Called by sc-orchestrator during Phase 2 when GraphQL schema files, resolvers, or GraphQL dependencies are detected.
## Phase 1: Discovery
### File Patterns to Search
```
**/*.graphql, **/*.gql, **/schema.*, **/typeDefs.*, **/resolvers.*,
**/*resolver*, **/*schema*, **/*graphql*, **/mutations/*, **/queries/*,
**/subscriptions/*, **/directives/*
```
### Keyword Patterns to Search
```
"typeDefs", "resolvers", "ApolloServer", "graphql-yoga", "makeExecutableSchema",
"buildSchema", "@Query", "@Mutation", "@Resolver", "GraphQLObjectType",
"introspection", "depthLimit", "costAnalysis", "complexityLimit",
"__schema", "__type", "subscription", "directive"
```
### Security Checks
**1. Introspection Enabled in Production**
- Check if introspection is explicitly disabled in production config
- Search for: `introspection: true`, missing introspection config, `NODE_ENV` checks
**2. Query Depth/Complexity Limits Missing**
- Check for depth limiting middleware: `graphql-depth-limit`, `depthLimit`, `@complexity`
- Check for query cost analysis: `graphql-query-complexity`, `costAnalysis`
- Absence of both = vulnerable to nested query DoS
**3. Batching Without Limits**
- Check if query batching is enabled without limits
- Search for: `allowBatchedHttpRequests`, batch query handler, array query acceptance
**4. Field-Level Authorization**
- Check if resolver functions verify user permissions
- Search for missing auth checks in mutation resolvers
- Check directive-based auth: `@auth`, `@hasRole`, `@authenticated`
**5. SQL/NoSQL Injection in Resolvers**
- Trace GraphQL arguments through resolver to database queries
- Check if arguments are used in raw queries without parameterization
**6. Information Disclosure via Error Messages**
- Check if detailed error messages are returned to clients
- Search for: `formatError`, `debug: true`, stack trace exposure
**7. Subscription Authorization**
- Check WebSocket subscription handlers for auth validation
- Verify subscription filters don't leak data to unauthorized users
## Phase 2: Verification
### Nested Query DoS Example
```graphql
# Attack: Deeply nested query (no depth limit)
query {
user(id: 1) {
posts {
author {
posts {
author {
posts { # ...repeating to depth 50+
title
}
}
}
}
}
}
}
```
```javascript
// VULNERABLE: No depth or complexity limits
const server = new ApolloServer({
typeDefs,
resolvers,
});
// SAFE: With depth and complexity limits
import depthLimit from 'graphql-depth-limit';
import { createComplexityLimitRule } from 'graphql-validation-complexity';
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
depthLimit(10),
createComplexityLimitRule(1000),
],
introspection: process.env.NODE_ENV !== 'production',
});
```
### Authorization Bypass Example
```javascript
// VULNERABLE: No auth check in resolver
const resolvers = {
Mutation: {
deleteUser: async (_, { id }, context) => {
return await User.findByIdAndDelete(id); // Anyone can delete any user
}
}
};
// SAFE: Auth check in resolver
const resolvers = {
Mutation: {
deleteUser: async (_, { id }, context) => {
if (!context.user || context.user.role !== 'admin') {
throw new ForbiddenError('Not authorized');
}
return await User.findByIdAndDelete(id);
}
}
};
```
## Severity Classification
- **Critical:** SQL/NoSQL injection through GraphQL arguments, authentication bypass in mutations
- **High:** Missing field-level authorization on sensitive mutations, introspection enabled exposing internal schema in production
- **Medium:** Missing query depth/complexity limits (DoS risk), batching abuse, subscription data leaks
- **Low:** Verbose error messages, introspection enabled in staging, missing rate limiting on queries
## Output Format
### Finding: GQL-{NNN}
- **Title:** {GraphQL vulnerability type}
- **Severity:** Critical | High | Medium | Low
- **Confidence:** 0-100
- **File:** file/path:line
- **Vulnerability Type:** CWE-89 (Injection) | CWE-200 (Information Disclosure) | CWE-862 (Missing Authorization) | CWE-770 (Resource Exhaustion)
- **Description:** {What was found}
- **Proof of Concept:** {Example query demonstrating the issue}
- **Impact:** {What happens if exploited}
- **Remediation:** {How to fix with code example}
- **References:** https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html
## Common False Positives
1. **Introspection in development** — introspection is expected in dev/staging environments
2. **Public queries without auth** — some queries are intentionally public (e.g., product listings)
3. **Depth limits in gateway** — depth limiting may be enforced at the API gateway level, not in the GraphQL server
4. **Schema-first codegen** — generated resolvers may appear to lack auth but are wrapped by middleware
5. **Federated schemas** — auth may be handled by the gateway in a federated GraphQL architecture
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!