← Back to the GraphQL Introspection Exposure Checker

GraphQL Introspection Exposure Checker vs. checking by hand

Pasting the standard __schema introspection query into GraphiQL, Postman, or a curl command and reading whether it comes back populated is a real, fast way to answer the yes/no question: does this endpoint answer an unauthenticated introspection query at all. It is also the method most developers already reach for, since every GraphQL server ships a playground that runs the identical query. This page names no product or company being compared against -- "checking by hand" is a generic manual habit, not a competing tool.

CaseChecking by handThis tool
Endpoint answers a plain introspection query with a populated schemaCorrectly reads as "enabled"Correctly reads as "enabled"
Endpoint returns an explicit "introspection is disabled" errorCorrectly reads as "disabled"Correctly reads as "disabled", and names the distinction from a dead or wrong-URL endpoint
Testing from a GraphiQL UI hosted on the API's own domainSame-origin request always succeeds -- says nothing about whether a third-party page could read the same responseRuns the request from this page's own origin, so a successful browser result demonstrates the target's CORS policy actually permits a cross-origin read
Schema exposes a mutation type buried among hundreds of query fieldsEasy to skim the default Query tab and never open MutationAlways parses mutationType.fields and subscriptionType.fields specifically and escalates the finding to danger the moment any mutation field exists
Field names matching sensitive wording (admin, resetPassword, impersonateUser)Requires reading every field name in a schema that can run to thousands of linesScans every query and mutation field name against a fixed sensitive-wording list and lists every match
Repeatable in CI on every deployManual, point-in-timeCLI returns a real exit code from the same classification

The gap that matters most: which origin actually asked

A GraphQL playground served by the API itself runs its introspection query from that API's own origin, and a same-origin request is never subject to CORS -- so success there proves only that the endpoint answers introspection queries at all, not that an attacker's page on a different origin could read the response too. This tool's browser check deliberately runs from a separate origin (this page), so a successful result means the target's own CORS configuration actually permits an arbitrary third-party site to read a schema dump from a visitor's browser -- the realistic exposure path, and one that testing from the API's own tooling cannot demonstrate either way. When the target's CORS policy blocks that read, the browser check correctly reports it cannot tell and the CLI -- never subject to CORS, since that restriction is enforced by browsers, not by Node's fetch -- still gets a definitive answer.

The gap that matters second: severity, not just yes/no

"Introspection is enabled" is one finding. Whether the exposed schema includes a mutation type -- the API's entire write surface, before an attacker has to guess a single field name -- is a materially different severity, and it is the one thing a person skimming a large schema by eye is most likely to miss, because GraphiQL's docs explorer opens on the Query type by default and Mutation is a separate branch to think to click into. This tool raises the finding to danger the moment mutationType.fields is non-empty, specifically so that severity does not depend on a reader remembering to open the second tab.

When to still just look

If you already know the endpoint answers introspection and you only want to browse the schema interactively -- read a specific type's fields, follow a resolver's arguments -- GraphiQL's docs explorer is the better tool for that; it is built for exploration, not for a pass/fail exposure verdict. The gap this tool closes is the two questions above: whether the exposure is reachable from an arbitrary origin, and whether the exposed surface includes writes, both of which a same-origin skim of the schema does not answer.

Common questions

What does pasting an introspection query into GraphiQL or curl actually show you?

It answers the yes/no question directly: send the standard __schema introspection query, and a populated response means introspection is enabled. That single request genuinely settles whether the endpoint answers at all -- the same request this tool sends.

Why does testing from GraphiQL hosted on the API's own domain understate the risk?

A GraphiQL or Playground UI served by the API itself makes its introspection request from that API's own origin, which any same-origin request is always allowed to do -- it says nothing about whether an arbitrary third-party page, running in a visitor's browser on a different origin, could run the same query and read the response. That is exactly what CORS governs, and it is the realistic exposure: a malicious or compromised page anywhere on the web probing the endpoint from a stranger's browser. This tool's browser check runs from this page's own origin and reports whether the target's CORS policy actually permits that cross-origin read, then falls back to a CLI (which is never subject to CORS) when it does not -- so a clean result names which of the two questions it answered rather than leaving CORS untested.

Why is spotting the mutation surface easy to miss by eye?

A real API's introspection response can list hundreds of types and enough fields that the raw JSON runs to thousands of lines. GraphiQL's docs explorer defaults to browsing the Query type, and mutationType is a separate branch of the same tree a reader has to think to open. This tool always parses mutationType.fields and subscriptionType.fields specifically, counts them, and raises the finding as danger rather than notice the moment any mutation field is present -- because an exposed write surface is a materially worse finding than an exposed read surface, and that distinction should not depend on a reader remembering to check the second tab.

Why check field names for sensitive wording automatically instead of reading the schema yourself?

Reading every field name in a schema of even moderate size for words like admin, resetPassword, or impersonateUser is tedious and easy to shortcut once the obvious top-level fields look ordinary. This tool scans every query and mutation field name against a fixed list of sensitive-operation wording and lists the matches -- still a hint to review by hand, not proof of a missing authorization check, since GraphQL field-level auth is common and no introspection response can show it. What the automated scan changes is coverage: it checks every field name, not just the ones a skim happens to land on.