Use only the Academy Lab on 127.0.0.1:5177. Do not copy these tests to a public system without explicit written authorization.
Build the secure query
Set POST /graphql, Content-Type application/json, select User A, and place the foreign object in variables.id.
{"query":"query Resource($id: ID!) { resource(id: $id) { id owner note } }","variables":{"id":"resource-b"}}HTTP 200 with data.resource null and an errors entry containing FORBIDDEN.
Switch to the vulnerable field
Change only resource to resourceInsecure and rerun the authorization matrix with User A and User B.
{"query":"query Resource($id: ID!) { resourceInsecure(id: $id) { id owner note } }","variables":{"id":"resource-b"}}HTTP 200 with User B’s resource data returned to User A.
Normalize dynamic fields
Exclude timestamps, trace identifiers, and session identifiers from comparison. Keep owner, tenant, note, GraphQL errors, and returned field paths in scope.
Confirm a second object
Run the vulnerable query again with resource-a under User B. Save both object results in one finding only when the behavior is the same.
Prove it before moving on.
You can explain why secure and vulnerable GraphQL responses both use HTTP 200 but have opposite authorization meanings.
