HarperFast / HarperFast/harper
GraphQL has no query depth/complexity/cost limit (DoS surface; dense-graph fan-out confirmed)
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
## Summary
The GraphQL endpoint enforces **no depth, complexity, or cost limit** on queries. `graphql.parse(query)` (`server/graphqlQuerying.ts:453`) is called without the library's available `maxTokens` cap.
- **Sparse graphs:** self-referential `@relationship` chains execute to depth 2048 at flat 2–4ms; the only limit is accidental — at depth ~20000 the recursive parse / `buildSelectQuery` (`graphqlQuerying.ts:286`) overflows the stack → HTTP 500 `Maximum call stack size exceeded` (caught; worker survives; no OOM). The error is opaque (no depth/field context).
- **Dense graphs (confirmed):** on a fully-connected graph, nested-query cost grows ~`fanout^depth` with the **entire** matched subtree serialized into one response and no server pushback — e.g. fanout=10 / depth 5 = 111,111 nodes in a single ~591ms response; walking past the data depth still costs seconds.
An attacker who can write a dense subgraph (or a naturally dense relationship domain) can force six-figure-node, multi-second single-request work with no rejection. No crash/OOM, but a real DoS lever.
## Suggestion
Add a configurable max selection depth + complexity/cost bound, and pass `maxTokens` to `graphql.parse`; return a clear `400` (with depth/field context) rather than a `500` stack overflow or unbounded serialization.
## Repro
`integrationTests/qa-scratch/graphql-depth.test.ts` (sparse + stack-overflow edge); `integrationTests/qa-scratch/graphql-dense.test.ts` (dense fan-out).
---
_Found via the exploratory QA campaign (qa-explorer), scenarios QA-036 + QA-039. Harper `001bf7b9c` (v5.1.0, main)._
Contributor guide
Assessment
This issue has not been assessed yet.