fix(backend): apply the large-body limit to every spelling Express routes
req.path was compared to the canonical '/api/voice/extract' only, but Express routes case-insensitively and ignores a trailing slash by default. '/api/voice/extract/' therefore reached the controller with the 100 kb parser, and 413'd every recording past ~20 seconds — a failure that reads as a broken microphone rather than a routing detail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,20 @@ describe('createJsonBodyParser', () => {
|
|||||||
expect((res.body as ProbeBody).keys).toBe(1);
|
expect((res.body as ProbeBody).keys).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('widens the limit for the spellings Express itself accepts', async () => {
|
||||||
|
// Express routes case-insensitively and ignores a trailing slash by default, so these
|
||||||
|
// all reach the voice controller. Any of them taking the 100 kb parser would 413 a
|
||||||
|
// real recording and read as a broken microphone.
|
||||||
|
for (const path of [
|
||||||
|
'/api/voice/extract/',
|
||||||
|
'/API/Voice/Extract',
|
||||||
|
'/api/Voice/extract/',
|
||||||
|
]) {
|
||||||
|
const res = await request(buildApp()).post(path).send(bodyOfKb(300));
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('does not widen the limit for a path that merely looks similar', async () => {
|
it('does not widen the limit for a path that merely looks similar', async () => {
|
||||||
for (const path of [
|
for (const path of [
|
||||||
'/api/voice/extract/extra',
|
'/api/voice/extract/extra',
|
||||||
|
|||||||
@@ -24,12 +24,22 @@ export const VOICE_BODY_LIMIT = '10mb';
|
|||||||
* the endpoint rejected every real recording with a 500. One explicit branch has no such
|
* the endpoint rejected every real recording with a 500. One explicit branch has no such
|
||||||
* coupling, and is covered by body-parsers.spec.ts.
|
* coupling, and is covered by body-parsers.spec.ts.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Express routes case-insensitively and ignores a trailing slash unless configured
|
||||||
|
* otherwise, so `/API/Voice/Extract/` reaches the same controller. Matching only the
|
||||||
|
* canonical spelling would hand those requests the 100 kb parser and 413 every real
|
||||||
|
* recording — a failure that looks like a broken microphone, not a routing detail.
|
||||||
|
*/
|
||||||
|
function isVoiceExtractPath(path: string): boolean {
|
||||||
|
return path.toLowerCase().replace(/\/+$/, '') === VOICE_EXTRACT_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
export function createJsonBodyParser(): RequestHandler {
|
export function createJsonBodyParser(): RequestHandler {
|
||||||
const voiceParser = json({ limit: VOICE_BODY_LIMIT });
|
const voiceParser = json({ limit: VOICE_BODY_LIMIT });
|
||||||
const defaultParser = json();
|
const defaultParser = json();
|
||||||
|
|
||||||
return (req: Request, res: Response, next: NextFunction) =>
|
return (req: Request, res: Response, next: NextFunction) =>
|
||||||
req.path === VOICE_EXTRACT_PATH
|
isVoiceExtractPath(req.path)
|
||||||
? voiceParser(req, res, next)
|
? voiceParser(req, res, next)
|
||||||
: defaultParser(req, res, next);
|
: defaultParser(req, res, next);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user