/** * @module @arcis/node/sanitizers/graphql * GraphQL injection prevention (sdk-vectors.md tier 1 #21). * * Two threats covered: * * 1. **Depth-bomb DoS** — nested-query payloads like * `query { x { x { x { ... } } } }` to ridiculous depth that explode * resolver work (each `{` typically maps to a database round-trip). * Even a 50-deep query against a real schema can hammer the * backend; 1000-deep crashes the resolver entirely. * * 2. **Introspection abuse** — `__schema` / `__type` / `__typename` * queries that let an attacker enumerate the entire schema, then * use that map to find sensitive fields, deprecated mutations, * or unprotected admin paths. Production GraphQL endpoints should * disable introspection by default. * * v1 is regex-based: count `{` / `}` for nesting depth (no escape * handling — strings inside the query that contain `{` will * over-count). False positives are an acceptable tradeoff for v1 * because (a) the depth threshold is well above legitimate query * shapes, (b) a real GraphQL parser pulls in `graphql` as a runtime * dep — significant for a sanitizer that ships in every Arcis * install. Customers running queries near the threshold can either * raise `maxDepth` or bring their own AST pre-pass. * * NOT included in v1: * - Field-count limit (some servers have this; orthogonal to depth) * - Alias-bomb detection (`q { f1: foo, f2: foo, ...}` — easier as * a length-check than a parse) * - Variable rebinding attacks * * Each is a follow-up if customers ask. Documented inline. */ export interface GraphqlGuardOptions { /** Maximum allowed nesting depth. Default: 10. Most legit queries are <8. */ maxDepth?: number; /** Maximum query string length in characters. Default: 10000. */ maxLength?: number; /** * Block introspection queries (`__schema`, `__type`). Default: true. * Set `false` in development if you rely on GraphiQL / Apollo * Studio. Production should leave this on. */ blockIntrospection?: boolean; /** * Maximum number of field aliases per query (`label: field`). * Default: 50. Alias-bomb attacks repeat the same expensive field * under many labels to multiply backend cost. Real queries rarely * use more than 20 aliases. improvements.md §1.2 V34. */ maxAliases?: number; /** * Reject queries whose fragment definitions form a cycle (direct * self-reference `fragment A on T { ...A }` or indirect * `A → B → A`). Such cycles either infinite-loop a naive resolver * or get rejected by `graphql-core` with a 500. Default: true. * improvements.md §1.2 V34. */ blockFragmentCycles?: boolean; } export type GraphqlViolation = 'depth' | 'length' | 'introspection' | 'aliases' | 'fragment_cycle'; export interface GraphqlGuardResult { /** True if the query violated any configured limit. */ blocked: boolean; /** Which limit fired first. Precedence: depth → introspection → aliases → fragment_cycle → length. */ reason?: GraphqlViolation; /** Observed nesting depth. Always returned. */ depth: number; /** Observed length. Always returned. */ length: number; /** Observed alias count (improvements.md §1.2 V34). Always returned. */ aliases: number; } /** * Inspect a GraphQL query against the configured limits. Returns a * structured result; the middleware below uses this directly. Pure * function — no I/O, no res handle. */ export declare function inspectGraphqlQuery(query: string, options?: GraphqlGuardOptions): GraphqlGuardResult; /** * Detect-only API matching the rest of the sanitizer module surface * (`detectXss` / `detectSql` / `detectXxe` / etc.). Returns a boolean * for callers that just want a yes/no — use `inspectGraphqlQuery` if * you need the structured reason. */ export declare function detectGraphqlAbuse(query: string, options?: GraphqlGuardOptions): boolean; //# sourceMappingURL=graphql.d.ts.map