mirror of
https://github.com/nestjs/nest.git
synced 2026-10-28 09:45:43 +08:00
fix(express): send object error bodies with a json content type
ExpressAdapter.reply() replaced a non-JSON Content-Type set on the
route (e.g. with @Header()) only when the body had a statusCode of 400
or more. A custom HttpException body, as in
new HttpException({ ... }, 404), need not have one, so res.json() sent
the error under the route's text/html type, and request text echoed in
the body was rendered as markup.
An object body sent with an error status code now gets the JSON
Content-Type too. Behaviour change: on a route with a non-JSON
Content-Type, such a response is labelled application/json and logs the
same warning as error bodies with a statusCode already did; text bodies
keep the route's type.
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-DGP0MUQQ.
This commit is contained in:
2 files changed
+35
-1
No files matched your search
@@ -190,7 +190,12 @@ export class ExpressAdapter extends AbstractHttpAdapter<
|
||||
if (
|
||||
typeof responseContentType === 'string' &&
|
||||
!isJsonContentType(responseContentType) &&
|
||||
body?.statusCode >= HttpStatus.BAD_REQUEST
|
||||
(body?.statusCode >= HttpStatus.BAD_REQUEST ||
|
||||
// A custom error body (e.g. "new HttpException({ ... }, 404)") need
|
||||
// not have a "statusCode" of its own
|
||||
(isObject(body) &&
|
||||
!isNil(statusCode) &&
|
||||
statusCode >= HttpStatus.BAD_REQUEST))
|
||||
) {
|
||||
this.logger.warn(
|
||||
"Content-Type doesn't match Reply body, you might need a custom ExceptionFilter for non-JSON responses",
|
||||
|
||||
@@ -327,6 +327,35 @@ describe('ExpressAdapter', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should force a JSON content type for error bodies without a "statusCode"', () => {
|
||||
const response = createResponse();
|
||||
response.getHeader.mockReturnValue('text/html');
|
||||
const warnSpy = vi
|
||||
.spyOn((expressAdapter as any).logger, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
// A custom HttpException body, e.g. new HttpException({ ... }, 404)
|
||||
const body = { status: 404, error: '<img src=x onerror=alert(1)>' };
|
||||
|
||||
expressAdapter.reply(response, body, 404);
|
||||
|
||||
expect(response.setHeader).toHaveBeenCalledWith(
|
||||
'Content-Type',
|
||||
'application/json',
|
||||
);
|
||||
expect(warnSpy).toHaveBeenCalled();
|
||||
expect(response.json).toHaveBeenCalledWith(body);
|
||||
});
|
||||
|
||||
it('should keep the content type of a text body sent with an error status', () => {
|
||||
const response = createResponse();
|
||||
response.getHeader.mockReturnValue('text/html');
|
||||
|
||||
expressAdapter.reply(response, '<h1>Not found</h1>', 404);
|
||||
|
||||
expect(response.setHeader).not.toHaveBeenCalled();
|
||||
expect(response.send).toHaveBeenCalledWith('<h1>Not found</h1>');
|
||||
});
|
||||
|
||||
describe('when the body is a StreamableFile', () => {
|
||||
const createStreamResponse = () =>
|
||||
Object.assign(new PassThrough(), {
|
||||
|
||||
Reference in new issue
Block a user