GraphQL Introspection (graphql-introspection)
Summary
Detects whether GraphQL schema introspection is exposed on the endpoint.
Classification
- Category:
API_HEALTH - Plugin id:
graphql-introspection - Version:
1.0.0 - Target types:
API - Weight class:
LIGHT - Risk level: not set on plugin object
- Browser required: No (HTTP / Node / other — see source)
- Tier / group: Tier 4 — API
Schedule
- scheduleHints:
{ defaultInterval: '0 */6 * * *', priority: 4 } - Prerequisites: None declared on this plugin.
Configurable inputs
Public keys are derived from inputSchema: z.object({ ... }) (underscore-prefixed keys are runtime-only and omitted here).
| Field | Notes |
|---|---|
url |
Optional GraphQL endpoint override; defaults to target.url. |
headers |
Extra headers (e.g. auth) merged with Content-Type: application/json and the TesteBot user agent. |
expectEnabled |
When true, introspection enabled → INFO confirmation; disabled → INFO mismatch finding. Default false. |
timeout |
Request timeout in ms (default 10000). |
Runtime / injected config
- None documented beyond standard worker context (
target,config,logger,reportProgress). Underscore-prefixed keys are internal.
Checks that produce findings
These checks run inside this plugin during a single test run. They are not separate tests in the database — each becomes a Finding linked to the Run for graphql-introspection.
| Check | Typical severity | Why |
|---|---|---|
| GraphQL introspection enabled in production | WARNING | Exposed schema reveals queries, mutations, and types — attackers can map the attack surface without guessing field names. |
Introspection enabled (when expectEnabled is true) |
INFO | Confirms introspection is on when you intentionally allow it (e.g. dev tooling or a federated schema registry). |
Introspection disabled (when expectEnabled is true but schema not returned) |
INFO | Flags config drift when your pipeline expects introspection (schema upload, GraphQL IDE) but the server blocks it. |
How each check works
- POST introspection query — Sends a fixed query requesting
__schema { queryType, mutationType, types { name kind } }as JSON toconfig.urlortarget.url. - Transport failure — Network or timeout errors return run status
failed, empty findings, and a summary message (no finding row). - Parse response — If the body is not valid JSON → run status
passed, score 100, metricintrospectionEnabled: unknown(treated as “likely disabled”). - Detect schema —
parsed.data.__schemapresent → introspection considered enabled; recordsschemaTypes,queryType, andmutationTypein metrics. - Production security finding — When introspection is enabled,
expectEnabled === false, andtarget.environment === 'PRODUCTION'→ WARNING finding with type count. - expectEnabled mode — When
expectEnabled === true: enabled → INFO with type count; disabled → INFO noting schema data missing. - Non-production + enabled + expectEnabled false — No finding (silent pass); only PRODUCTION targets get the security WARNING.
- Score — WARNING finding → score 50; otherwise 100.
Caveats and concerns
- PRODUCTION-only security alert — STAGING/DEV targets with introspection enabled produce no finding when
expectEnabledis false, even though the exposure exists. - Invalid JSON → pass — HTML error pages, WAF blocks, or plain-text responses that are not JSON are interpreted as “introspection likely disabled” with a perfect score — possible false negative.
- Reachability failure → failed run, no finding — Unlike health-check plugins, connection errors do not create a structured finding for alerting deduplication.
- POST only — Servers that accept introspection on GET but reject POST may appear disabled.
- No persisted-query / APQ bypass — Only standard
__schemaintrospection is tested; other schema leak vectors are out of scope. - Auth-dependent endpoints — Without correct
headers, authenticated-only GraphQL may return errors that look like disabled introspection.
Source
packages/test-plugins/src/plugins/graphql-introspection.ts