Priority: Medium · Difficulty: Medium · Area: GraphQL / API security · Est. effort: 6–9 h
📌 Problem
src/graphql/ is small but exposed:
src/graphql/gateway.ts 356 lines
src/graphql/schema.ts 132 lines
Against that, tests/graphql/ contains a single file, gateway.test.ts.
The line counts are not the concern by themselves — the concern is that a GraphQL endpoint has a specific and well-known set of abuse surfaces, and a single test file for a 356-line gateway is unlikely to cover them. GraphQL differs from REST here: a single endpoint accepts arbitrarily-shaped queries, so protections that REST gets from routing must be implemented explicitly.
The first deliverable is an audit, not a guess. Establish which of the following the gateway implements, and which of those are actually tested:
- Query depth limiting — without it, a recursive query can be nested arbitrarily deep and exhaust CPU or memory.
- Query complexity/cost analysis — depth alone does not bound a wide query.
- Introspection policy — is introspection enabled in production, and is that intended?
- Error masking — do resolver errors leak stack traces, SQL, or internal messages to the client?
- Authorisation — is it enforced per-resolver, or only at the transport layer? A single unguarded resolver is a full bypass.
- Batching/aliasing limits — aliasing the same field many times multiplies work without increasing depth.
- Rate limiting — the repo has
src/redis/rateLimitStore.ts; does it apply to the GraphQL route?
🎯 Design decision required
State and defend:
- The audit result — a table of the surfaces above: implemented and tested / implemented but untested / not implemented. This is the core of the PR.
- Prioritisation. You are unlikely to close every gap in one PR. Rank them by exploitability against this specific schema and fix the top ones. Argue the ranking from the schema, not from a generic checklist.
- Defaults. For anything you add — a depth limit, a complexity budget — justify the specific number against the shapes the schema actually permits, not a value copied from a blog post.
🧩 Requirements and context
- Do not change the schema's public shape. This is about protecting it, not redesigning it.
- Every protection you add or confirm needs a test that demonstrates it rejecting an abusive query, not merely accepting a normal one.
- Reuse existing infrastructure —
src/redis/rateLimitStore.ts, src/validation/, the metrics registry — rather than introducing parallel mechanisms.
- Error masking must not lose server-side detail: the client gets a safe message, the logs keep the full error.
- Legitimate queries must keep working. Any limit you set must not break existing consumers; check
tests/graphql/gateway.test.ts and the generated SDKs.
🛠️ Suggested execution
- Read
gateway.ts and schema.ts and produce the audit table. Put it in the PR first.
- Write tests that attempt abuse for each surface, and record what currently happens.
- Fix the highest-ranked gaps.
- Confirm legitimate queries are unaffected.
- List remaining gaps for follow-up.
✅ Acceptance criteria
🚫 Out of scope
- Changing the GraphQL schema's public shape.
- Replacing the GraphQL server library.
- REST route protections.
🧪 Verification
pnpm install --frozen-lockfile
pnpm test tests/graphql
pnpm typecheck && pnpm lint
pnpm check:sdk:ts # confirm generated SDK queries still validate
📤 What your PR must include
- The audit table.
- Your prioritisation and the schema-specific reasoning behind it.
- Justification for any numeric limit you chose.
- The abuse tests, showing rejection.
Closes #<n>.
🔒 Security notes
A GraphQL endpoint concentrates risk: one route, arbitrary query shapes, and per-resolver authorisation that is easy to apply inconsistently. Depth and complexity limits are denial-of-service controls, error masking is an information-disclosure control, and per-resolver authorisation is the actual access-control boundary. Establishing which of these exist today is worth more than adding any one of them blindly.
📋 Guidelines
- Minimum 95% test coverage on changed lines
- Clear documentation
- Timeframe: 96 hours from assignment
- One logical change per commit; no merge commits
💬 Join our community
Working on this, or want to sanity-check your approach before you start? Come and ask — the maintainers are there and happy to help.
Telegram: https://t.me/Grainlify
Priority: Medium · Difficulty: Medium · Area: GraphQL / API security · Est. effort: 6–9 h
📌 Problem
src/graphql/is small but exposed:Against that,
tests/graphql/contains a single file,gateway.test.ts.The line counts are not the concern by themselves — the concern is that a GraphQL endpoint has a specific and well-known set of abuse surfaces, and a single test file for a 356-line gateway is unlikely to cover them. GraphQL differs from REST here: a single endpoint accepts arbitrarily-shaped queries, so protections that REST gets from routing must be implemented explicitly.
The first deliverable is an audit, not a guess. Establish which of the following the gateway implements, and which of those are actually tested:
src/redis/rateLimitStore.ts; does it apply to the GraphQL route?🎯 Design decision required
State and defend:
🧩 Requirements and context
src/redis/rateLimitStore.ts,src/validation/, the metrics registry — rather than introducing parallel mechanisms.tests/graphql/gateway.test.tsand the generated SDKs.🛠️ Suggested execution
gateway.tsandschema.tsand produce the audit table. Put it in the PR first.✅ Acceptance criteria
pnpm typecheck,pnpm lintandpnpm testpass.🚫 Out of scope
🧪 Verification
📤 What your PR must include
Closes #<n>.🔒 Security notes
A GraphQL endpoint concentrates risk: one route, arbitrary query shapes, and per-resolver authorisation that is easy to apply inconsistently. Depth and complexity limits are denial-of-service controls, error masking is an information-disclosure control, and per-resolver authorisation is the actual access-control boundary. Establishing which of these exist today is worth more than adding any one of them blindly.
📋 Guidelines
💬 Join our community
Working on this, or want to sanity-check your approach before you start? Come and ask — the maintainers are there and happy to help.
Telegram: https://t.me/Grainlify