Skip to content

Commit

Permalink
fix: [#1585] Fixes security vulnerability that allowed for server sid…
Browse files Browse the repository at this point in the history
…e code to be executed by a <script> tag (#1586)

* fix: [#1585] Fixes security vulnerability that allowed for server side code to be executed by a <script> tag

* chore: [#1585] Fixes unit test
  • Loading branch information
capricorn86 authored Nov 6, 2024
1 parent a20dba9 commit 5ee0b16
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class SyncFetchScriptBuilder {
}): string {
const sortedHeaders = {};
const headerNames = Object.keys(request.headers).sort();

for (const name of headerNames) {
sortedHeaders[name] = request.headers[name];
}
Expand All @@ -43,7 +44,7 @@ export default class SyncFetchScriptBuilder {
null,
4
)};
const request = sendRequest('${request.url.href}', options, (incomingMessage) => {
const request = sendRequest(\`${request.url.href}\`, options, (incomingMessage) => {
let data = Buffer.alloc(0);
incomingMessage.on('data', (chunk) => {
data = Buffer.concat([data, Buffer.from(chunk)]);
Expand Down
66 changes: 66 additions & 0 deletions packages/happy-dom/test/fetch/SyncFetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,72 @@ describe('SyncFetch', () => {
expect(response.body.toString()).toBe(responseText);
});

it('Should not allow to inject code into scripts executed using child_process.execFileSync().', () => {
browserFrame.url = 'https://localhost:8080/';

const url =
"https://localhost:8080/`+require('child_process').execSync('id')+`/'+require('child_process').execSync('id')+'";
const responseText = 'test';

mockModule('child_process', {
execFileSync: (
command: string,
args: string[],
options: { encoding: string; maxBuffer: number }
) => {
expect(command).toEqual(process.argv[0]);
expect(args[0]).toBe('-e');
expect(args[1]).toBe(
SyncFetchScriptBuilder.getScript({
url: new URL(
"https://localhost:8080/%60+require('child_process').execSync('id')+%60/'+require('child_process').execSync('id')+'"
),
method: 'GET',
headers: {
Accept: '*/*',
Connection: 'close',
Referer: 'https://localhost:8080/',
'User-Agent': window.navigator.userAgent,
'Accept-Encoding': 'gzip, deflate, br'
},
body: null
})
);
// new URL() will convert ` into %60
// By using ` for the URL string within the script, we can prevent the script from being injected
expect(
args[1].includes(
`\`https://localhost:8080/%60+require('child_process').execSync('id')+%60/'+require('child_process').execSync('id')+'\``
)
).toBe(true);
expect(options).toEqual({
encoding: 'buffer',
maxBuffer: 1024 * 1024 * 1024
});
return JSON.stringify({
error: null,
incomingMessage: {
statusCode: 200,
statusMessage: 'OK',
rawHeaders: [],
data: Buffer.from(responseText).toString('base64')
}
});
}
});

const response = new SyncFetch({
browserFrame,
window,
url,
init: {
method: 'GET'
}
}).send();

expect(response.body.toString()).toBe(responseText);
});

it('Should send custom key/value object request headers.', () => {
browserFrame.url = 'https://localhost:8080/';

Expand Down

0 comments on commit 5ee0b16

Please sign in to comment.