mirror of
https://github.com/tenrok/axios.git
synced 2026-06-20 20:00:40 +03:00
* fix(sec): cve-2024-39338 (#6539) * fix(sec): fix test
This commit is contained in:
@@ -229,7 +229,7 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
|||||||
|
|
||||||
// Parse url
|
// Parse url
|
||||||
const fullPath = buildFullPath(config.baseURL, config.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];
|
const protocol = parsed.protocol || supportedProtocols[0];
|
||||||
|
|
||||||
if (protocol === 'data:') {
|
if (protocol === 'data:') {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
* @returns {boolean} True if the specified URL is absolute, otherwise false
|
* @returns {boolean} True if the specified URL is absolute, otherwise false
|
||||||
*/
|
*/
|
||||||
export default function isAbsoluteURL(url) {
|
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
|
// 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.
|
// 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ describe('helpers::isAbsoluteURL', function () {
|
|||||||
expect(isAbsoluteURL('!valid://example.com/')).toBe(false);
|
expect(isAbsoluteURL('!valid://example.com/')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if URL is protocol-relative', function () {
|
it('should return true if URL is protocol-relative', function () {
|
||||||
expect(isAbsoluteURL('//example.com/')).toBe(false);
|
expect(isAbsoluteURL('//example.com/')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if URL is relative', function () {
|
it('should return false if URL is relative', function () {
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
import axios from '../../../index.js';
|
import axios from '../../../index.js';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
import utils from '../../../lib/utils.js';
|
||||||
|
import platform from '../../../lib/platform/index.js';
|
||||||
|
|
||||||
|
|
||||||
const GOOD_PORT = 4666;
|
const GOOD_PORT = 4666;
|
||||||
const BAD_PORT = 4667;
|
const BAD_PORT = 4667;
|
||||||
@@ -27,7 +30,7 @@ describe('Server-Side Request Forgery (SSRF)', () => {
|
|||||||
badServer.close();
|
badServer.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not fetch bad server', async () => {
|
it('should not fetch in server-side mode', async () => {
|
||||||
const ssrfAxios = axios.create({
|
const ssrfAxios = axios.create({
|
||||||
baseURL: 'http://localhost:' + String(GOOD_PORT),
|
baseURL: 'http://localhost:' + String(GOOD_PORT),
|
||||||
});
|
});
|
||||||
@@ -36,10 +39,43 @@ describe('Server-Side Request Forgery (SSRF)', () => {
|
|||||||
// Malicious payload is as below.
|
// Malicious payload is as below.
|
||||||
const userId = '/localhost:' + String(BAD_PORT);
|
const userId = '/localhost:' + String(BAD_PORT);
|
||||||
|
|
||||||
const response = await ssrfAxios.get(`/${userId}`);
|
try {
|
||||||
assert.strictEqual(response.data, 'good');
|
await ssrfAxios.get(`/${userId}`);
|
||||||
assert.strictEqual(response.config.baseURL, 'http://localhost:' + String(GOOD_PORT));
|
} catch (error) {
|
||||||
assert.strictEqual(response.config.url, '//localhost:' + String(BAD_PORT));
|
assert.ok(error.message.startsWith('Invalid URL'));
|
||||||
assert.strictEqual(response.request.res.responseUrl, 'http://localhost:' + String(GOOD_PORT) + '/localhost:' + String(BAD_PORT));
|
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) + '/');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user