Skip to content
Merged
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
20 changes: 18 additions & 2 deletions spec/PagesRouter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1396,15 +1396,31 @@ describe('Pages Router', () => {
expect(response.text).toContain('<img');
});

it('should escape XSS in locale parameter', async () => {
it('should reject XSS payload in locale parameter', async () => {
const xssLocale = '"><svg/onload=alert(1)>';
const response = await request({
url: `http://localhost:8378/1/apps/choose_password?locale=${encodeURIComponent(xssLocale)}&appId=test`,
});

expect(response.status).toBe(200);
// Invalid locale is rejected by format validation, so the XSS
// payload never reaches the page content
expect(response.text).not.toContain('<svg/onload=alert(1)>');
expect(response.text).toContain('&quot;&gt;&lt;svg');
expect(response.text).not.toContain('&quot;&gt;&lt;svg');
});

it('should reject non-ASCII characters in locale parameter', async () => {
// Non-ASCII characters like ğ (U+011F) would cause ERR_INVALID_CHAR
// when set as HTTP header value if not rejected by locale validation
const nonAsciiLocale = 'ğ';
const response = await request({
url: `http://localhost:8378/1/apps/choose_password?locale=${encodeURIComponent(nonAsciiLocale)}&appId=test`,
});

expect(response.status).toBe(200);
// Non-ASCII locale is rejected by format validation;
// no ERR_INVALID_CHAR error occurs
expect(response.headers['x-parse-page-param-locale']).toBeUndefined();
});

it('should handle legitimate usernames with quotes correctly', async () => {
Expand Down
10 changes: 10 additions & 0 deletions src/Routers/PagesRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,16 @@ export class PagesRouter extends PromiseRouter {
(req.body || {})[pageParams.locale] ||
(req.params || {})[pageParams.locale] ||
(req.headers || {})[pageParamHeaderPrefix + pageParams.locale];

// Validate locale format to prevent path traversal and invalid
// HTTP header characters; only allow standard locale patterns
// like "en", "en-US", "de-AT", "zh-Hans-CN"
if (locale !== undefined && typeof locale !== 'string') {
return undefined;
}
if (typeof locale === 'string' && !/^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/.test(locale)) {
return undefined;
}
return locale;
}

Expand Down
Loading