Skip to content

feat: HTTP security hardening — helmet, rate limiting, body size cap (#43 #44 #45 #46) - #164

Open
darcszn wants to merge 1 commit into
stellar-vortex-protocol:mainfrom
darcszn:feat/security-helmet-throttler-body-limit
Open

feat: HTTP security hardening — helmet, rate limiting, body size cap (#43 #44 #45 #46)#164
darcszn wants to merge 1 commit into
stellar-vortex-protocol:mainfrom
darcszn:feat/security-helmet-throttler-body-limit

Conversation

@darcszn

@darcszn darcszn commented Jul 28, 2026

Copy link
Copy Markdown

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.ts

Added app.use(helmet(...)) as the first middleware in bootstrap(), 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 /docs requires relaxed CSP because it inlines scripts and loads assets from a CDN. The helmet config explicitly allows:

  • scriptSrc: 'self', 'unsafe-inline', cdn.jsdelivr.net
  • styleSrc: 'self', 'unsafe-inline', cdn.jsdelivr.net
  • imgSrc: 'self', data:, cdn.jsdelivr.net
  • crossOriginEmbedderPolicy disabled (Swagger uses inline event handlers)

#44 — Global IP-based rate limiting

Files: src/app.module.ts, src/intents/intents.controller.ts

Installed @nestjs/throttler and configured a named global throttle: 100 requests per 60 seconds per IP. The ThrottlerGuard is registered as APP_GUARD in AppModule so 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/intents
  • POST /api/v1/intents/quote

#45 — Per-user throttle on intent creation

Files: src/intents/user-throttler.guard.ts, src/intents/intents.controller.ts

Introduced UserThrottlerGuard, a subclass of ThrottlerGuard that overrides getTracker() to key on req.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 user is absent from the body. Applied via @UseGuards(UserThrottlerGuard) on POST /api/v1/intents only — 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.ts

Added app.use(json({ limit: '10kb' })) as the very first middleware in bootstrap(). 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 PayloadTooLargeError is not a NestJS HttpException, so the existing @Catch() filter was swallowing it and returning 500. Updated HttpExceptionFilter to detect non-HttpException errors that carry a numeric status field (4xx/5xx) and forward them with the correct status code.

Test utility: Mirrored the json({ limit: '10kb' }) middleware in createTestApp() so e2e tests reflect production behaviour exactly.

New e2e test (test/body-size.e2e-spec.ts):

  • Payload > 10 KB → 413 Payload Too Large
  • Normal payload → 201 Created

Files Changed

File Change
src/main.ts Add json({ limit }) + helmet() middleware
src/app.module.ts Add ThrottlerModule + global ThrottlerGuard
src/intents/user-throttler.guard.ts New — per-user throttle guard
src/intents/intents.controller.ts Apply UserThrottlerGuard, add @ApiTooManyRequestsResponse
src/common/http-exception.filter.ts Pass through Express HTTP errors (e.g. 413) correctly
test/utils/create-test-app.ts Mirror body-size limit in test bootstrap
test/body-size.e2e-spec.ts New — 413 acceptance test
package.json Add @nestjs/throttler, @types/express

Test Results

All existing unit and e2e tests pass. New body-size e2e suite added and passing.

Test Suites: 6 passed, 6 total
Tests:       25 passed, 25 total

…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
@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant