Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* fix(sec): cve-2024-39338 (axios#6539)

* fix(sec): fix test
  • Loading branch information
levpachmanov authored Aug 13, 2024
1 parent 07a661a commit 6b6b605
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 11 deletions.
2 changes: 1 addition & 1 deletion lib/adapters/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export default isHttpAdapterSupported && function httpAdapter(config) {

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

if (protocol === 'data:') {
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/isAbsoluteURL.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
* @returns {boolean} True if the specified URL is absolute, otherwise false
*/
export default function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://".
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)\/\//i.test(url);
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
}
4 changes: 2 additions & 2 deletions test/specs/helpers/isAbsoluteURL.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ describe('helpers::isAbsoluteURL', function () {
expect(isAbsoluteURL('!valid://example.com/')).toBe(false);
});

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

it('should return false if URL is relative', function () {
Expand Down
48 changes: 42 additions & 6 deletions test/unit/regression/SNYK-JS-AXIOS-7361793.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import axios from '../../../index.js';
import http from 'http';
import assert from 'assert';
import utils from '../../../lib/utils.js';
import platform from '../../../lib/platform/index.js';


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

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

const response = await ssrfAxios.get(`/${userId}`);
assert.strictEqual(response.data, 'good');
assert.strictEqual(response.config.baseURL, 'http://localhost:' + String(GOOD_PORT));
assert.strictEqual(response.config.url, '//localhost:' + String(BAD_PORT));
assert.strictEqual(response.request.res.responseUrl, 'http://localhost:' + String(GOOD_PORT) + '/localhost:' + String(BAD_PORT));
try {
await ssrfAxios.get(`/${userId}`);
} catch (error) {
assert.ok(error.message.startsWith('Invalid URL'));
return;
}
assert.fail('Expected an error to be thrown');
});

describe('should fetch in client-side mode', () => {
let hasBrowserEnv, origin;

before(() => {
hasBrowserEnv = utils.hasBrowserEnv;
origin = platform.origin;
utils.hasBrowserEnv = true;
platform.origin = 'http://localhost:' + String(GOOD_PORT);
});
after(() => {
utils.hasBrowserEnv = hasBrowserEnv;
platform.origin = origin;
});
it('should fetch in client-side mode', async () => {
utils.hasBrowserEnv = true;
const ssrfAxios = axios.create({
baseURL: 'http://localhost:' + String(GOOD_PORT),
});

// Good payload would be `userId = '12345'`
// Malicious payload is as below.
const userId = '/localhost:' + String(BAD_PORT);

const response = await ssrfAxios.get(`/${userId}`);
assert.strictEqual(response.data, 'bad');
assert.strictEqual(response.config.baseURL, 'http://localhost:' + String(GOOD_PORT));
assert.strictEqual(response.config.url, '//localhost:' + String(BAD_PORT));
assert.strictEqual(response.request.res.responseUrl, 'http://localhost:' + String(BAD_PORT) + '/');
});
});
});

0 comments on commit 6b6b605

Please sign in to comment.