Skip to content

Commit 64aeecf

Browse files
committed
fix: Sanitize control characters in page parameter response headers
Strip control characters from page parameter header values to prevent ERR_INVALID_CHAR errors when parameters contain CRLF or other control characters. The sanitization is applied regardless of the encodePageParamHeaders setting.
1 parent 80db912 commit 64aeecf

File tree

2 files changed

+51
-14
lines changed

2 files changed

+51
-14
lines changed

spec/PagesRouter.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,32 @@ describe('Pages Router', () => {
10401040
}).catch(e => e);
10411041
expect(response.status).not.toBe(500);
10421042
});
1043+
1044+
it('does not return 500 when page parameter contains CRLF characters', async () => {
1045+
await reconfigureServer(config);
1046+
const crlf = 'abc\r\nX-Injected: 1';
1047+
const url = `${config.publicServerURL}/apps/choose_password?appId=test&token=${encodeURIComponent(crlf)}&username=testuser`;
1048+
const response = await request({
1049+
url: url,
1050+
followRedirects: false,
1051+
}).catch(e => e);
1052+
expect(response.status).not.toBe(500);
1053+
expect(response.status).toBe(200);
1054+
});
1055+
1056+
it('does not return 500 when page parameter contains CRLF characters in redirect response', async () => {
1057+
await reconfigureServer(config);
1058+
const crlf = 'abc\r\nX-Injected: 1';
1059+
const url = `${config.publicServerURL}/apps/test/resend_verification_email`;
1060+
const response = await request({
1061+
method: 'POST',
1062+
url: url,
1063+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
1064+
body: `username=${encodeURIComponent(crlf)}`,
1065+
followRedirects: false,
1066+
}).catch(e => e);
1067+
expect(response.status).not.toBe(500);
1068+
});
10431069
});
10441070

10451071
describe('custom route', () => {

src/Routers/PagesRouter.js

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -455,13 +455,7 @@ export class PagesRouter extends PromiseRouter {
455455

456456
// Add placeholders in header to allow parsing for programmatic use
457457
// of response, instead of having to parse the HTML content.
458-
const encode = this.pagesConfig.encodePageParamHeaders;
459-
const headers = Object.entries(params).reduce((m, p) => {
460-
if (p[1] !== undefined) {
461-
m[`${pageParamHeaderPrefix}${p[0].toLowerCase()}`] = encode ? encodeURIComponent(p[1]) : p[1];
462-
}
463-
return m;
464-
}, {});
458+
const headers = this.composePageParamHeaders(params);
465459

466460
return { text: data, headers: headers };
467461
}
@@ -555,6 +549,29 @@ export class PagesRouter extends PromiseRouter {
555549
return locale;
556550
}
557551

552+
/**
553+
* Composes page parameter headers from the given parameters. Control
554+
* characters are always stripped from header values to prevent
555+
* ERR_INVALID_CHAR errors. Values are URI-encoded if the
556+
* `encodePageParamHeaders` option is enabled.
557+
* @param {Object} params The parameters to include in the headers.
558+
* @returns {Object} The headers object.
559+
*/
560+
composePageParamHeaders(params) {
561+
const encode = this.pagesConfig.encodePageParamHeaders;
562+
return Object.entries(params).reduce((m, p) => {
563+
if (p[1] !== undefined) {
564+
let value = encode ? encodeURIComponent(p[1]) : p[1];
565+
if (typeof value === 'string') {
566+
// eslint-disable-next-line no-control-regex
567+
value = value.replace(/[\x00-\x1f\x7f]/g, '');
568+
}
569+
m[`${pageParamHeaderPrefix}${p[0].toLowerCase()}`] = value;
570+
}
571+
return m;
572+
}, {});
573+
}
574+
558575
/**
559576
* Creates a response with http redirect.
560577
* @param {Object} req The express request.
@@ -578,13 +595,7 @@ export class PagesRouter extends PromiseRouter {
578595

579596
// Add parameters to header to allow parsing for programmatic use
580597
// of response, instead of having to parse the HTML content.
581-
const encode = this.pagesConfig.encodePageParamHeaders;
582-
const headers = Object.entries(params).reduce((m, p) => {
583-
if (p[1] !== undefined) {
584-
m[`${pageParamHeaderPrefix}${p[0].toLowerCase()}`] = encode ? encodeURIComponent(p[1]) : p[1];
585-
}
586-
return m;
587-
}, {});
598+
const headers = this.composePageParamHeaders(params);
588599

589600
return {
590601
status: 303,

0 commit comments

Comments
 (0)