Skip to content

Commit 6b6b605

Browse files
authored
fix(sec): CVE-2024-39338 (#6539) (#6543)
* fix(sec): cve-2024-39338 (#6539) * fix(sec): fix test
1 parent 07a661a commit 6b6b605

File tree

4 files changed

+47
-11
lines changed

4 files changed

+47
-11
lines changed

lib/adapters/http.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
229229

230230
// Parse url
231231
const fullPath = buildFullPath(config.baseURL, config.url);
232-
const parsed = new URL(fullPath, 'http://localhost');
232+
const parsed = new URL(fullPath, utils.hasBrowserEnv ? platform.origin : undefined);
233233
const protocol = parsed.protocol || supportedProtocols[0];
234234

235235
if (protocol === 'data:') {

lib/helpers/isAbsoluteURL.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
* @returns {boolean} True if the specified URL is absolute, otherwise false
99
*/
1010
export default function isAbsoluteURL(url) {
11-
// A URL is considered absolute if it begins with "<scheme>://".
11+
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
1212
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
1313
// by any combination of letters, digits, plus, period, or hyphen.
14-
return /^([a-z][a-z\d+\-.]*:)\/\//i.test(url);
14+
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
1515
}

test/specs/helpers/isAbsoluteURL.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ describe('helpers::isAbsoluteURL', function () {
1212
expect(isAbsoluteURL('!valid://example.com/')).toBe(false);
1313
});
1414

15-
it('should return false if URL is protocol-relative', function () {
16-
expect(isAbsoluteURL('//example.com/')).toBe(false);
15+
it('should return true if URL is protocol-relative', function () {
16+
expect(isAbsoluteURL('//example.com/')).toBe(true);
1717
});
1818

1919
it('should return false if URL is relative', function () {

test/unit/regression/SNYK-JS-AXIOS-7361793.js

+42-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import axios from '../../../index.js';
55
import http from 'http';
66
import assert from 'assert';
7+
import utils from '../../../lib/utils.js';
8+
import platform from '../../../lib/platform/index.js';
9+
710

811
const GOOD_PORT = 4666;
912
const BAD_PORT = 4667;
@@ -27,7 +30,7 @@ describe('Server-Side Request Forgery (SSRF)', () => {
2730
badServer.close();
2831
});
2932

30-
it('should not fetch bad server', async () => {
33+
it('should not fetch in server-side mode', async () => {
3134
const ssrfAxios = axios.create({
3235
baseURL: 'http://localhost:' + String(GOOD_PORT),
3336
});
@@ -36,10 +39,43 @@ describe('Server-Side Request Forgery (SSRF)', () => {
3639
// Malicious payload is as below.
3740
const userId = '/localhost:' + String(BAD_PORT);
3841

39-
const response = await ssrfAxios.get(`/${userId}`);
40-
assert.strictEqual(response.data, 'good');
41-
assert.strictEqual(response.config.baseURL, 'http://localhost:' + String(GOOD_PORT));
42-
assert.strictEqual(response.config.url, '//localhost:' + String(BAD_PORT));
43-
assert.strictEqual(response.request.res.responseUrl, 'http://localhost:' + String(GOOD_PORT) + '/localhost:' + String(BAD_PORT));
42+
try {
43+
await ssrfAxios.get(`/${userId}`);
44+
} catch (error) {
45+
assert.ok(error.message.startsWith('Invalid URL'));
46+
return;
47+
}
48+
assert.fail('Expected an error to be thrown');
49+
});
50+
51+
describe('should fetch in client-side mode', () => {
52+
let hasBrowserEnv, origin;
53+
54+
before(() => {
55+
hasBrowserEnv = utils.hasBrowserEnv;
56+
origin = platform.origin;
57+
utils.hasBrowserEnv = true;
58+
platform.origin = 'http://localhost:' + String(GOOD_PORT);
59+
});
60+
after(() => {
61+
utils.hasBrowserEnv = hasBrowserEnv;
62+
platform.origin = origin;
63+
});
64+
it('should fetch in client-side mode', async () => {
65+
utils.hasBrowserEnv = true;
66+
const ssrfAxios = axios.create({
67+
baseURL: 'http://localhost:' + String(GOOD_PORT),
68+
});
69+
70+
// Good payload would be `userId = '12345'`
71+
// Malicious payload is as below.
72+
const userId = '/localhost:' + String(BAD_PORT);
73+
74+
const response = await ssrfAxios.get(`/${userId}`);
75+
assert.strictEqual(response.data, 'bad');
76+
assert.strictEqual(response.config.baseURL, 'http://localhost:' + String(GOOD_PORT));
77+
assert.strictEqual(response.config.url, '//localhost:' + String(BAD_PORT));
78+
assert.strictEqual(response.request.res.responseUrl, 'http://localhost:' + String(BAD_PORT) + '/');
79+
});
4480
});
4581
});

0 commit comments

Comments
 (0)