Skip to content

Commit 088c0e2

Browse files
authored
Merge branch 'alpha' into fix/pages-locale-path-traversal
Signed-off-by: Manuel <[email protected]>
2 parents c51afd2 + cd0ec81 commit 088c0e2

File tree

5 files changed

+82
-31
lines changed

5 files changed

+82
-31
lines changed

changelogs/CHANGELOG_alpha.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# [9.6.0-alpha.38](https://github.com/parse-community/parse-server/compare/9.6.0-alpha.37...9.6.0-alpha.38) (2026-03-18)
2+
3+
4+
### Bug Fixes
5+
6+
* Sanitize control characters in page parameter response headers ([#10237](https://github.com/parse-community/parse-server/issues/10237)) ([337ffd6](https://github.com/parse-community/parse-server/commit/337ffd65ccf94495a54cd883c5e8fa7a3892606c))
7+
8+
# [9.6.0-alpha.37](https://github.com/parse-community/parse-server/compare/9.6.0-alpha.36...9.6.0-alpha.37) (2026-03-18)
9+
10+
11+
### Bug Fixes
12+
13+
* Security fix fast-xml-parser from 5.5.5 to 5.5.6 ([#10235](https://github.com/parse-community/parse-server/issues/10235)) ([f521576](https://github.com/parse-community/parse-server/commit/f521576143336334aad2cbac82c3f368afe8f706))
14+
115
# [9.6.0-alpha.36](https://github.com/parse-community/parse-server/compare/9.6.0-alpha.35...9.6.0-alpha.36) (2026-03-18)
216

317

package-lock.json

Lines changed: 16 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "parse-server",
3-
"version": "9.6.0-alpha.36",
3+
"version": "9.6.0-alpha.38",
44
"description": "An express module providing a Parse-compatible API server",
55
"main": "lib/index.js",
66
"repository": {

spec/PagesRouter.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,32 @@ describe('Pages Router', () => {
10961096
await fs.rm(targetDir, { recursive: true, force: true });
10971097
}
10981098
});
1099+
1100+
it('does not return 500 when page parameter contains CRLF characters', async () => {
1101+
await reconfigureServer(config);
1102+
const crlf = 'abc\r\nX-Injected: 1';
1103+
const url = `${config.publicServerURL}/apps/choose_password?appId=test&token=${encodeURIComponent(crlf)}&username=testuser`;
1104+
const response = await request({
1105+
url: url,
1106+
followRedirects: false,
1107+
}).catch(e => e);
1108+
expect(response.status).not.toBe(500);
1109+
expect(response.status).toBe(200);
1110+
});
1111+
1112+
it('does not return 500 when page parameter contains CRLF characters in redirect response', async () => {
1113+
await reconfigureServer(config);
1114+
const crlf = 'abc\r\nX-Injected: 1';
1115+
const url = `${config.publicServerURL}/apps/test/resend_verification_email`;
1116+
const response = await request({
1117+
method: 'POST',
1118+
url: url,
1119+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
1120+
body: `username=${encodeURIComponent(crlf)}`,
1121+
followRedirects: false,
1122+
}).catch(e => e);
1123+
expect(response.status).not.toBe(500);
1124+
});
10991125
});
11001126

11011127
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
}
@@ -561,6 +555,29 @@ export class PagesRouter extends PromiseRouter {
561555
return locale;
562556
}
563557

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

585602
// Add parameters to header to allow parsing for programmatic use
586603
// of response, instead of having to parse the HTML content.
587-
const encode = this.pagesConfig.encodePageParamHeaders;
588-
const headers = Object.entries(params).reduce((m, p) => {
589-
if (p[1] !== undefined) {
590-
m[`${pageParamHeaderPrefix}${p[0].toLowerCase()}`] = encode ? encodeURIComponent(p[1]) : p[1];
591-
}
592-
return m;
593-
}, {});
604+
const headers = this.composePageParamHeaders(params);
594605

595606
return {
596607
status: 303,

0 commit comments

Comments
 (0)