P0: Add GraphQL query-cost controls via graphql-armor (#164)#183
Open
dkijania wants to merge 1 commit into
Open
P0: Add GraphQL query-cost controls via graphql-armor (#164)#183dkijania wants to merge 1 commit into
dkijania wants to merge 1 commit into
Conversation
The public GraphQL endpoint had no query-cost controls. The list fields (`events`, `actions`, `blocks`) return unbounded lists, so a deeply-nested or heavily-aliased query is a trivial denial-of-service against the backing Postgres — it can be made arbitrarily expensive before any limit applies. Add graphql-armor validation plugins, wired ahead of execution in `buildPlugins`, with conservative, env-tunable limits: - GRAPHQL_MAX_DEPTH (selection-set nesting, default 10) - GRAPHQL_MAX_ALIASES (aliases per operation, default 15) - GRAPHQL_MAX_TOKENS (lexical tokens per document, default 1000) - GRAPHQL_MAX_COST (depth/field cost heuristic, default 5000) Field suggestions are always blocked so error messages don't leak schema shape (complementing `useDisableIntrospection`); introspection is ignored by the depth/cost rules so GraphiQL still works when explicitly enabled. The deepest query this API legitimately serves is ~5 levels, well within the defaults. Malformed env values fall back to the safe default rather than disabling a protection. The individual `@escape.tech/graphql-armor-*` plugins are used (not the meta package) because the meta package requires @envelop/core v5 while this stack is pinned to v4 (Yoga 4); the sub-plugins have no conflicting peer deps. The full graphql-armor meta integration can follow the Yoga 5 upgrade (#176). Unit tests cover config parsing/fallbacks and prove end-to-end through Yoga that an over-depth query is rejected before execution. Docs, env example, and env type declarations updated. Closes #164. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSuak9smCHbp4N17xjjLF6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Part of the production-readiness epic (#163). Closes #164.
The public GraphQL endpoint had no query-cost controls. The list fields (
events,actions,blocks) return unbounded lists, so a deeply-nested or heavily-aliased query is a trivial DoS against the backing Postgres — it can be made arbitrarily expensive before any limit applies. This is the highest-impact P0: the "crafted query = outage" mitigation.Changes
src/server/graphql-armor.ts— builds graphql-armor validation plugins from conservative, env-tunable limits (isolated and unit-testable).buildPluginsruns them ahead of execution, so abusive shapes are rejected during validation.GRAPHQL_MAX_DEPTH10GRAPHQL_MAX_ALIASES15GRAPHQL_MAX_TOKENS1000GRAPHQL_MAX_COST5000useDisableIntrospection). Introspection is ignored by the depth/cost rules so GraphiQL still works when explicitly enabled.Why the individual plugins, not the meta package
@escape.tech/graphql-armor(meta) requires@envelop/corev5, but this stack is pinned to v4 (Yoga 4). The individual@escape.tech/graphql-armor-*sub-plugins have no conflicting peer deps and work on the v4 stack. Switching to the meta integration can follow the Yoga 5 upgrade (#176).Testing
npm run build— cleannpm run test:unit— all pass, including an end-to-end test through Yoga asserting an over-depth query is rejected before execution, plus config parsing/fallback testsnpm run lint— cleannpx prettier --debug-check .— exit 0🤖 Generated with Claude Code