feat: add abuse protection and request limits for anonymous build publishing #118
+346
−6
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.
Purpose
The
publishBuildmutation is public, with no authentication, rate limiting or quotas, so anyone could flood the database with spam. This PR adds server-side abuse protection while keeping publishing anonymous, and adds limits on GraphQL request cost.Changes
publishBuildper client IP: 5 attempts per 10 minutes, usingrate-limiter-flexible. The check runs first in the resolver, so blocked requests create no database entries. Exceeding it returns HTTP429with aRetry-Afterheader and error codeRATE_LIMITED. Every attempt counts, including invalid ones and each alias in a single request.limits.ts) that rejects operations nested deeper than 8 levels or selecting more than 100 fields (HTTP400). Fragments are expanded when counting; introspection is not counted.413with a JSON error).TRUST_PROXYsetting so the real visitor IP is used behind Apache.backend/server/src/config.ts. OnlyTRUST_PROXYis read from.env.TRUST_PROXYin.env.example.backend/server/src/test/.resolvers.test.tsto mockconfig.js, becauseresolvers.tsnow imports the rate limiter, which loads the real config.Deployment note: set
TRUST_PROXY=1in the VM'sbackend/server/.env, and make sure port 3001 is not reachable from outside the VM. WithoutTRUST_PROXY, all visitors share the proxy's address and one limit. The rate limit counters are kept in memory and reset when the backend restarts.Related issues
Closes #89
Verification
Automated checks, run from
backend/server:npm test: all 20 tests pass, including 10 new ones. They cover the depth and field limits (including fragments, fragment cycles and introspection) and the publish limit (normal publishing works, excess attempts are blocked with no database queries, each IP is limited separately, invalid attempts count).Manually tested 6 publish build attempts. 5 were successfull, the 6th showed an error (see screenshot)
Screenshots
Checklist