diff --git a/strix/prompts/protocols/graphql.jinja b/strix/prompts/protocols/graphql.jinja new file mode 100644 index 0000000..335675c --- /dev/null +++ b/strix/prompts/protocols/graphql.jinja @@ -0,0 +1,215 @@ + +GRAPHQL — ADVANCED TESTING AND EXPLOITATION + +GraphQL’s flexibility enables powerful data access, but also unique failures: field- and edge-level authorization drift, schema exposure (even with introspection off), alias/batch abuse, resolver injection, federated trust gaps, and complexity/fragment bombs. Bind subject→action→object at resolver boundaries and validate across every transport and feature flag. + + +- Queries, mutations, subscriptions (graphql-ws, graphql-transport-ws) +- Persisted queries/Automatic Persisted Queries (APQ) +- Federation (Apollo/GraphQL Mesh): _service SDL and _entities +- File uploads (GraphQL multipart request spec) +- Relay conventions: global node IDs, connections/cursors + + + +1. Fingerprint endpoint(s), transport(s), and stack (framework, plugins, gateway). Note GraphiQL/Playground exposure and CORS/credentials. +2. Obtain multiple principals (unauth, basic, premium, admin/staff) and capture at least one valid object ID per subject. +3. Acquire schema via introspection; if disabled, infer iteratively from errors, field suggestions, __typename probes, vocabulary brute-force. +4. Build an Actor × Operation × Type/Field matrix. Exercise each resolver path with swapped IDs, roles, tenants, and channels (REST proxies, GraphQL HTTP, WS). +5. Validate consistency: same authorization and validation across queries, mutations, subscriptions, batch/alias, persisted queries, and federation. + + + + +- Common paths: /graphql, /api/graphql, /v1/graphql, /gql +- Probe with minimal canary: +{% raw %} +POST /graphql {"query":"{__typename}"} +GET /graphql?query={__typename} +{% endraw %} +- Detect GraphiQL/Playground; note if accessible cross-origin and with credentials. + + + +- If enabled, dump full schema; otherwise: + - Use __typename on candidate fields to confirm types + - Abuse field suggestions and error shapes to enumerate names/args + - Infer enums from “expected one of” errors; coerce types by providing wrong shapes + - Reconstruct edges from pagination and connection hints (pageInfo, edges/node) + + + +- Map root operations, object types, interfaces/unions, directives (@auth, @defer, @stream), and custom scalars (Upload, JSON, DateTime) +- Identify sensitive fields: email, tokens, roles, billing, file keys, admin flags +- Note cascade paths where child resolvers may skip auth under parent assumptions + + + + + +- Test field-level and edge-level checks, not just top-level gates. Pair owned vs foreign IDs within the same request via aliases to diff responses. +{% raw %} +query { + me { id } + a: order(id:"A_OWNER") { id total owner { id email } } + b: order(id:"B_FOREIGN") { id total owner { id email } } +} +{% endraw %} +- Probe mutations for partial updates that bypass validation (JSON Merge Patch semantics in inputs). +- Validate node/global ID resolvers (Relay) bind to the caller; decode/replace base64 IDs and compare access. + + + +- Alias to perform many logically separate reads in one operation; watch for per-request vs per-field auth discrepancies +- If array batching is supported (non-standard), submit multiple operations to bypass rate limits and achieve partial failures +{% raw %} +query { + u1:user(id:"1"){email} + u2:user(id:"2"){email} + u3:user(id:"3"){email} +} +{% endraw %} + + + +- Scalars vs objects vs arrays: {% raw %}{id:123}{% endraw} vs {% raw %}{id:"123"}{% endraw} vs {% raw %}{id:[123]}{% endraw}; send null/empty/0/-1 and extra object keys retained by backend +- Duplicate keys in JSON variables: {% raw %}{"id":1,"id":2}{% endraw} (parser precedence), default argument values, coercion errors leaking field names + + + +- Decode cursors (often base64) to manipulate offsets/IDs and skip filters +- Abuse selection sets and fragments to force overfetching of sensitive subfields + + + +- GraphQL multipart: test multiple Upload scalars, filename/path tricks, unexpected content-types, oversize chunks; verify server-side ownership/scoping for returned URLs + + + + + +- Field suggestion leakage: submit near-miss names to harvest suggestions +- Error taxonomy: different codes/messages for unknown field vs unauthorized field reveal existence +- __typename sprinkling on edges to confirm types without schema + + + +- Use @defer and @stream to obtain partial results or subtrees hidden by parent checks; confirm server supports incremental delivery +{% raw %} +query @defer { + me { id } + ... @defer { adminPanel { secrets } } +} +{% endraw %} + + + +- Recursive fragment spreads and wide selection sets cause CPU/memory spikes; craft minimal reproducible bombs to validate cost limits +{% raw %} +fragment x on User { friends { ...x } } +query { me { ...x } } +{% endraw %} +- Validate depth/complexity limiting, query cost analyzers, and timeouts + + + +- Apollo Federation: query _service { sdl } if exposed; target _entities to materialize foreign objects by key without proper auth in subgraphs +{% raw %} +query { + _entities(representations:[ + {__typename:"User", id:"TARGET"} + ]) { ... on User { email roles } } +} +{% endraw %} +- Look for auth done at gateway but skipped in subgraph resolvers; cross-subgraph IDOR via inconsistent ownership checks + + + +- Check message-level authorization, not only handshake; attempt to subscribe to channels for other users/tenants; test cross-tenant event leakage +- Abuse filter args in subscription resolvers to reference foreign IDs + + + +- APQ hashes can be guessed/bruteforced or leaked from clients; replay privileged operations by supplying known hashes with attacker variables +- Validate that hash→operation mapping enforces principal and operation allowlists + + + +- If cookie-auth is used and GET is accepted, test CSRF on mutations via query parameters; verify SameSite and origin checks +- Cross-origin GraphiQL/Playground exposure with credentials can leak data via postMessage bridges + + + +- Reshape queries: comments, block strings, Unicode escapes, alias/fragment indirection, JSON variables vs inline args, GET vs POST vs application/graphql +- Split fields across fragments and inline spreads to avoid naive signatures + + + + + +- Toggle content-types: application/json, application/graphql, multipart/form-data; try GET with query and variables params +- HTTP/2 multiplexing and connection reuse to widen timing windows and rate limits + + + +- Case/underscore variations, Unicode homoglyphs (server-dependent), aliases masking sensitive field names + + + +- CDN/key confusion: responses cached without considering Authorization or variables; manipulate Vary and Accept headers +- Redirects and 304/206 behaviors leaking partially cached GraphQL responses + + + + + +- node(id:…) global resolution: decode base64, swap type/id pairs, ensure per-type authorization is enforced inside resolvers +- Connections: verify that filters (owner/tenant) apply before pagination; cursor tampering should not cross ownership boundaries + + + +- Custom directives (@auth, @private) and plugins often annotate intent but do not enforce; verify actual checks in each resolver path + + + + +- GraphQL + IDOR: enumerate IDs via list fields, then fetch or mutate foreign objects +- GraphQL + CSRF: trigger mutations cross-origin when cookies/auth are accepted without proper checks +- GraphQL + SSRF: resolvers that fetch URLs (webhooks, metadata) abused to reach internal services + + + +1. Provide paired requests (owner vs non-owner) differing only in identifiers/roles that demonstrate unauthorized access or mutation. +2. Prove resolver-level bypass: show top-level checks present but child field/edge exposes data. +3. Demonstrate transport parity: reproduce via HTTP and WS (subscriptions) or via persisted queries. +4. Minimize payloads; document exact selection sets and variable shapes used. + + + +- Introspection available only on non-production/stub endpoints +- Public fields by design with documented scopes +- Aggregations or counts without sensitive attributes +- Properly enforced depth/complexity and per-resolver authorization across transports + + + +- Cross-account/tenant data exposure and unauthorized state changes +- Bypass of federation boundaries enabling lateral access across services +- Credential/session leakage via lax CORS/CSRF around GraphiQL/Playground + + + +1. Always diff the same operation under multiple principals with aliases in one request. +2. Sprinkle __typename to map types quickly when schema is hidden. +3. Attack edges: child resolvers often skip auth compared to parents. +4. Try @defer/@stream and subscriptions to slip gated data in incremental events. +5. Decode cursors and node IDs; assume base64 unless proven otherwise. +6. Federation: exercise _entities with crafted representations; subgraphs frequently trust gateway auth. +7. Persisted queries: extract hashes from clients; replay with attacker variables. +8. Keep payloads small and structured; restructure rather than enlarge to evade WAFs. +9. Validate defenses by code/config review where possible; don’t trust directives alone. +10. Prove impact with role-separated, transport-separated, minimal PoCs. + + +GraphQL security is resolver security. If any resolver on the path to a field fails to bind subject, object, and action, the graph leaks. Validate every path, every transport, every environment. +