feat: HTTP security hardening — helmet, rate limiting, body size cap (#43 #44 #45 #46) - #164
Open
darcszn wants to merge 1 commit into
Conversation
…body size cap Resolves stellar-vortex-protocol#43, stellar-vortex-protocol#44, stellar-vortex-protocol#45, stellar-vortex-protocol#46 - stellar-vortex-protocol#43: helmet baseline HTTP security headers - stellar-vortex-protocol#44: global IP-based rate limiting via @nestjs/throttler - stellar-vortex-protocol#45: per-user throttle on POST /api/v1/intents - stellar-vortex-protocol#46: explicit 10 KB JSON body size limit with 413 e2e test
|
@darcszn Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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.
Summary
This PR addresses four security hardening issues across the bootstrap layer, module configuration, and intent creation endpoint.
Closes #43
Closes #44
Closes #45
Closes #46
Issues Resolved
#43 — Helmet baseline HTTP security headers
File:
src/main.tsAdded
app.use(helmet(...))as the first middleware inbootstrap(), before body parser and all other middleware. This sets standard security headers on every response (X-Content-Type-Options,X-Frame-Options,Strict-Transport-Security,X-XSS-Protection, etc.).Swagger UI at
/docsrequires relaxed CSP because it inlines scripts and loads assets from a CDN. The helmet config explicitly allows:scriptSrc:'self','unsafe-inline',cdn.jsdelivr.netstyleSrc:'self','unsafe-inline',cdn.jsdelivr.netimgSrc:'self',data:,cdn.jsdelivr.netcrossOriginEmbedderPolicydisabled (Swagger uses inline event handlers)#44 — Global IP-based rate limiting
Files:
src/app.module.ts,src/intents/intents.controller.tsInstalled
@nestjs/throttlerand configured a namedglobalthrottle: 100 requests per 60 seconds per IP. TheThrottlerGuardis registered asAPP_GUARDinAppModuleso it applies automatically to every route without any per-controller decoration.@ApiTooManyRequestsResponse(HTTP 429) is documented in Swagger on the two highest-traffic endpoints:POST /api/v1/intentsPOST /api/v1/intents/quote#45 — Per-user throttle on intent creation
Files:
src/intents/user-throttler.guard.ts,src/intents/intents.controller.tsIntroduced
UserThrottlerGuard, a subclass ofThrottlerGuardthat overridesgetTracker()to key onreq.body.user(the Stellar address, lowercased) instead of the remote IP. This closes the IP-rotation bypass vector: a single actor flooding intent creation with rotating IPs is still bounded by their user address.Limit: 10 intent creations per user per 60 seconds. Falls back to IP when
useris absent from the body. Applied via@UseGuards(UserThrottlerGuard)onPOST /api/v1/intentsonly — stacks on top of the global IP guard.#46 — Explicit JSON body size cap
Files:
src/main.ts,src/common/http-exception.filter.ts,test/utils/create-test-app.ts,test/body-size.e2e-spec.tsAdded
app.use(json({ limit: '10kb' }))as the very first middleware inbootstrap(). DTOs in this API (addresses, amounts, symbols) are tiny; the default multi-MB limit was unnecessarily permissive and a memory/abuse risk given the unbounded in-memory intent store.Exception filter fix: Express's
PayloadTooLargeErroris not a NestJSHttpException, so the existing@Catch()filter was swallowing it and returning 500. UpdatedHttpExceptionFilterto detect non-HttpExceptionerrors that carry a numericstatusfield (4xx/5xx) and forward them with the correct status code.Test utility: Mirrored the
json({ limit: '10kb' })middleware increateTestApp()so e2e tests reflect production behaviour exactly.New e2e test (
test/body-size.e2e-spec.ts):Files Changed
src/main.tsjson({ limit })+helmet()middlewaresrc/app.module.tsThrottlerModule+ globalThrottlerGuardsrc/intents/user-throttler.guard.tssrc/intents/intents.controller.tsUserThrottlerGuard, add@ApiTooManyRequestsResponsesrc/common/http-exception.filter.tstest/utils/create-test-app.tstest/body-size.e2e-spec.tspackage.json@nestjs/throttler,@types/expressTest Results
All existing unit and e2e tests pass. New body-size e2e suite added and passing.