Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions backend/src/__tests__/errorHandling.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { jest } from "@jest/globals";

Check failure on line 1 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"@jest/globals"` with `'@jest/globals'`
import request from "supertest";

Check failure on line 2 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"supertest"` with `'supertest'`
import { Keypair } from "@stellar/stellar-sdk";

Check failure on line 3 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"@stellar/stellar-sdk"` with `'@stellar/stellar-sdk'`
import { generateJwtToken } from "../services/authService.js";

Check failure on line 4 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"../services/authService.js"` with `'../services/authService.js'`
import app from "../app.js";

Check failure on line 5 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"../app.js"` with `'../app.js'`

jest.setTimeout(20000);

process.env.JWT_SECRET = "test-jwt-secret-min-32-chars-long!!";

Check failure on line 9 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"test-jwt-secret-min-32-chars-long!!"` with `'test-jwt-secret-min-32-chars-long!!'`
const authHeader = `Bearer ${generateJwtToken(Keypair.random().publicKey())}`;

describe("Centralized Error Handling", () => {

Check failure on line 12 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"Centralized·Error·Handling"` with `'Centralized·Error·Handling'`
/* ── 404 Not Found ────────────────────────────────────────── */

describe('404 catch-all', () => {
Expand Down Expand Up @@ -37,10 +37,10 @@

/* ── Validation Errors (backward compatibility) ───────────── */

describe("Zod validation errors", () => {

Check failure on line 40 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"Zod·validation·errors"` with `'Zod·validation·errors'`
it("should return 400 with validation failed message and error code", async () => {

Check failure on line 41 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"should·return·400·with·validation·failed·message·and·error·code"` with `'should·return·400·with·validation·failed·message·and·error·code'`
const response = await request(app)
.post("/api/simulate")

Check failure on line 43 in backend/src/__tests__/errorHandling.test.ts

View workflow job for this annotation

GitHub Actions / backend

Replace `"/api/simulate"` with `'/api/simulate'`
.set("Authorization", authHeader)
.send({});

Expand Down Expand Up @@ -75,6 +75,25 @@
});
});

describe("Request Payload Size Limit", () => {
it("should return 413 when payload exceeds the configured limit", async () => {
// Create a payload larger than 100kb
const largePayload = {
data: "x".repeat(1024 * 150) // 150kb string
};

const response = await request(app)
.post("/api/simulate")
.set("Authorization", authHeader)
.send(largePayload);

expect(response.status).toBe(413);
expect(response.body.success).toBe(false);
expect(response.body.error.code).toBe('VALIDATION_ERROR');
expect(response.body.error.message).toMatch(/payload too large/i);
});
});

/* ── Consistent JSON structure ────────────────────────────── */

describe('Response structure consistency', () => {
Expand Down
3 changes: 2 additions & 1 deletion backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ const corsOptions: cors.CorsOptions = {

app.use(cors(corsOptions));
app.use(compression());
app.use(express.json());
// Explicit request body size limit set to 100kb to mitigate payload-based DoS and unbounded audit log writes.
app.use(express.json({ limit: '100kb' }));
app.use(globalRateLimiter);
app.use(requestIdMiddleware);
app.use(requestLogger);
Expand Down
13 changes: 13 additions & 0 deletions backend/src/middleware/errorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,19 @@
return;
}

// ── Payload Too Large (body-parser) ────────────────────────
if ('type' in err && (err as any).type === 'entity.too.large') {

Check warning on line 98 in backend/src/middleware/errorHandler.ts

View workflow job for this annotation

GitHub Actions / backend

Unexpected any. Specify a different type
res.status(413).json({
success: false,
message: 'Request payload too large',
error: {
code: ErrorCode.VALIDATION_ERROR, // Or a dedicated code if defined
message: 'Request payload too large',
},
});
return;
}

// ── Unexpected / Programming Errors ──────────────────────────
logger.error('Unhandled error', {
requestId: req.requestId,
Expand Down
10 changes: 10 additions & 0 deletions pr_body_1184.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Closes #1184

### What does this PR do?
This PR enforces an explicit 100kb payload size limit on `express.json()` and correctly handles resulting `entity.too.large` errors so they return a structured 413 response rather than defaulting to an unhandled 500 error.

### Description
- **Explicit Size Limit:** Added an explicit `{ limit: '100kb' }` configuration to `express.json()` in `app.ts`. This protects the application and audit logs from unbounded payload sizes, while remaining more than generous enough to accommodate legitimate signed transaction payloads.
- **Centralized Error Handling:** Updated `errorHandler.ts` to natively catch `entity.too.large` errors emitted by `body-parser` and translate them into standard `413 Payload Too Large` responses with the `VALIDATION_ERROR` code.
- **Test Coverage:** Added an integration test in `errorHandling.test.ts` to assert that a 150kb payload correctly trips the limit and returns the structured `413` error.
- **Documentation:** Added inline comments describing the rationale behind the payload limit in `app.ts`.
Loading