strix/skills/protocols/graphql.md
Security testing for GraphQL APIs. Focus on resolver-level authorization, field/edge access control, batching abuse, and federation trust boundaries.
Operations
Transports
application/json or application/graphqlSchema Features
__schema, __type)@defer, @stream, custom auth directives (@auth, @private)Architecture
_service, _entitiesEndpoint Discovery
POST /graphql {"query":"{__typename}"}
POST /api/graphql {"query":"{__typename}"}
POST /v1/graphql {"query":"{__typename}"}
POST /gql {"query":"{__typename}"}
GET /graphql?query={__typename}
Check for GraphiQL/Playground exposure with credentials enabled (cross-origin with cookies can leak data via postMessage bridges).
Schema Acquisition
If introspection enabled:
{__schema{types{name fields{name args{name}}}}}
If disabled, infer schema via:
__typename probes on candidate fieldsSchema Mapping
Map: root operations, object types, interfaces/unions, directives, custom scalars. Identify sensitive fields: email, tokens, roles, billing, API keys, admin flags, file URLs. Note cascade paths where child resolvers may skip auth under parent assumptions.
Field-Level IDOR
Test with aliases comparing owned vs foreign objects in single request:
query {
own: order(id:"OWNED_ID") { id total owner { email } }
foreign: order(id:"FOREIGN_ID") { id total owner { email } }
}
Edge/Child Resolver Gaps
Parent resolver checks auth, child resolver assumes it's already validated:
query {
user(id:"FOREIGN") {
id
privateData { secrets } # Child may skip auth check
}
}
Relay Node Resolution
Decode base64 global IDs, swap type/id pairs:
query {
node(id:"VXNlcjoxMjM=") { ... on User { email } }
}
Ensure per-type authorization is enforced inside resolvers. Verify connection filters (owner/tenant) apply before pagination; cursor tampering should not cross ownership boundaries.
Mutation Bypass
Enumeration via Aliases
query {
u1:user(id:"1"){email}
u2:user(id:"2"){email}
u3:user(id:"3"){email}
}
Bypasses per-request rate limits; exposes per-field vs per-request auth inconsistencies.
Array Batching
If supported (non-standard), submit multiple operations to achieve partial failures and bypass limits.
Type Confusion
{id: 123} vs {id: "123"}
{id: [123]} vs {id: null}
{id: 0} vs {id: -1}
Duplicate Keys
{"id": 1, "id": 2}
Parser precedence varies; may bypass validation. Also test default argument values.
Extra Fields
Send unexpected keys in input objects; backends may pass them to resolvers or downstream logic.
Decode cursors (usually base64) to:
@defer/@stream
query {
me { id }
... @defer { adminPanel { secrets } }
}
May return gated data in incremental delivery. Confirm server supports incremental delivery.
Custom Directives
@auth, @private and similar directives often annotate intent but do not enforce—verify actual checks in each resolver path.
Fragment Bombs
fragment x on User { friends { ...x } }
query { me { ...x } }
Test depth/complexity limits, query cost analyzers, timeouts.
Wide Selection Sets
Abuse selection sets and fragments to force overfetching of sensitive subfields.
SDL Exposure
query { _service { sdl } }
Entity Materialization
query {
_entities(representations:[
{__typename:"User", id:"TARGET_ID"}
]) { ... on User { email roles } }
}
Gateway may enforce auth; subgraph resolvers may not. Look for cross-subgraph IDOR via inconsistent ownership checks.
GraphQL multipart spec:
Query Reshaping
"""...""")application/graphqlFragment Splitting
Split fields across fragments and inline spreads to avoid naive signatures:
fragment a on User { email }
fragment b on User { password }
query { me { ...a ...b } }
Transport Switching
Content-Type: application/json
Content-Type: application/graphql
Content-Type: multipart/form-data
GET with query params
Timing & Rate Limits
Naming Tricks
Cache Confusion
_service and _entities for subgraph auth gaps_entities accessing data without subgraph auth