'use strict'
const { describe, test } = require('node:test')
const util = require('../../lib/web/fetch/util')
const { HeadersList } = require('../../lib/web/fetch/headers')
test('responseURL', (t) => {
t.plan(2)
t.assert.ok(util.responseURL({
urlList: [
new URL('http://asd'),
new URL('http://fgh')
]
}))
t.assert.ok(!util.responseURL({
urlList: []
}))
})
test('responseLocationURL', (t) => {
t.plan(3)
const acceptHeaderList = new HeadersList()
acceptHeaderList.append('Accept', '*/*')
const locationHeaderList = new HeadersList()
locationHeaderList.append('Location', 'http://asd')
t.assert.ok(!util.responseLocationURL({
status: 200
}))
t.assert.ok(!util.responseLocationURL({
status: 301,
headersList: acceptHeaderList
}))
t.assert.ok(util.responseLocationURL({
status: 301,
headersList: locationHeaderList,
urlList: [
new URL('http://asd'),
new URL('http://fgh')
]
}))
})
test('requestBadPort', (t) => {
t.plan(3)
t.assert.strictEqual('allowed', util.requestBadPort({
urlList: [new URL('https://asd')]
}))
t.assert.strictEqual('blocked', util.requestBadPort({
urlList: [new URL('http://asd:7')]
}))
t.assert.strictEqual('blocked', util.requestBadPort({
urlList: [new URL('https://asd:7')]
}))
})
// https://html.spec.whatwg.org/multipage/origin.html#same-origin
// look at examples
test('sameOrigin', async (t) => {
await t.test('first test', (t) => {
const A = {
protocol: 'https:',
hostname: 'example.org',
port: ''
}
const B = {
protocol: 'https:',
hostname: 'example.org',
port: ''
}
t.assert.ok(util.sameOrigin(A, B))
})
await t.test('second test', (t) => {
const A = {
protocol: 'https:',
hostname: 'example.org',
port: '314'
}
const B = {
protocol: 'https:',
hostname: 'example.org',
port: '420'
}
t.assert.ok(!util.sameOrigin(A, B))
})
await t.test('obviously shouldn\'t be equal', (t) => {
t.assert.ok(!util.sameOrigin(
{ protocol: 'http:', hostname: 'example.org' },
{ protocol: 'https:', hostname: 'example.org' }
))
t.assert.ok(!util.sameOrigin(
{ protocol: 'https:', hostname: 'example.org' },
{ protocol: 'https:', hostname: 'example.com' }
))
})
await t.test('file:// urls', (t) => {
// urls with opaque origins should return true
const a = new URL('file:///C:/undici')
const b = new URL('file:///var/undici')
t.assert.ok(util.sameOrigin(a, b))
})
})
test('isURLPotentiallyTrustworthy', (t) => {
// https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-let-localhost-be-localhost#section-5.2
const valid = [
'http://localhost',
'http://localhost.',
'http://127.0.0.1',
'http://[::1]',
'https://something.com',
'wss://hello.com',
'data:text/plain;base64,randomstring',
'about:blank',
'about:srcdoc',
'http://subdomain.localhost',
'http://subdomain.localhost.',
'http://adb.localhost',
'http://localhost.localhost',
'blob:http://example.com/550e8400-e29b-41d4-a716-446655440000'
]
const invalid = [
'http://localhost.example.com',
'http://subdomain.localhost.example.com',
'file:///link/to/file.txt',
'http://121.3.4.5:55',
'null:8080',
'something:8080'
]
t.plan(valid.length + invalid.length + 1)
t.assert.ok(!util.isURLPotentiallyTrustworthy('string'))
for (const url of valid) {
const instance = new URL(url)
t.assert.ok(util.isURLPotentiallyTrustworthy(instance), instance)
}
for (const url of invalid) {
const instance = new URL(url)
t.assert.ok(!util.isURLPotentiallyTrustworthy(instance))
}
})
describe('setRequestReferrerPolicyOnRedirect', () => {
[
[
'should ignore empty string as policy',
'origin, asdas, asdaw34, no-referrer,,',
'no-referrer'
],
[
'should set referrer policy from response headers on redirect',
'origin',
'origin'
],
[
'should select the first valid policy from a response',
'asdas, origin',
'origin'
],
[
'should select the first valid policy from a response#2',
'no-referrer, asdas, origin, 0943sd',
'origin'
],
[
'should pick the last fallback over invalid policy tokens',
'origin, asdas, asdaw34',
'origin'
],
[
'should set not change request referrer policy if no Referrer-Policy from initial redirect response',
null,
'no-referrer, strict-origin-when-cross-origin'
],
[
'should set not change request referrer policy if the policy is a non-valid Referrer Policy',
'asdasd',
'no-referrer, strict-origin-when-cross-origin'
],
[
'should set not change request referrer policy if the policy is a non-valid Referrer Policy #2',
'asdasd, asdasa, 12daw,',
'no-referrer, strict-origin-when-cross-origin'
]
].forEach(([title, responseReferrerPolicy, expected]) => {
test(title, (t) => {
t.plan(1)
const request = {
referrerPolicy: 'no-referrer, strict-origin-when-cross-origin'
}
const actualResponse = {
headersList: new HeadersList()
}
actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
if (responseReferrerPolicy) {
actualResponse.headersList.append('Referrer-Policy', responseReferrerPolicy)
}
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)
t.assert.strictEqual(request.referrerPolicy, expected)
})
})
})
describe('urlHasHttpsScheme', () => {
const { urlHasHttpsScheme } = util
test('should return false for http url', (t) => {
t.assert.strictEqual(urlHasHttpsScheme('http://example.com'), false)
})
test('should return true for https url', (t) => {
t.assert.strictEqual(urlHasHttpsScheme('https://example.com'), true)
})
test('should return false for http object', (t) => {
t.assert.strictEqual(urlHasHttpsScheme({ protocol: 'http:' }), false)
})
test('should return true for https object', (t) => {
t.assert.strictEqual(urlHasHttpsScheme({ protocol: 'https:' }), true)
})
})
describe('isValidHeaderValue', () => {
const { isValidHeaderValue } = util
test('should return true for valid string', (t) => {
t.assert.strictEqual(isValidHeaderValue('valid123'), true)
t.assert.strictEqual(isValidHeaderValue('va lid123'), true)
t.assert.strictEqual(isValidHeaderValue('va\tlid123'), true)
})
test('should return false for string containing NUL', (t) => {
t.assert.strictEqual(isValidHeaderValue('invalid\0'), false)
t.assert.strictEqual(isValidHeaderValue('in\0valid'), false)
t.assert.strictEqual(isValidHeaderValue('\0invalid'), false)
})
test('should return false for string containing CR', (t) => {
t.assert.strictEqual(isValidHeaderValue('invalid\r'), false)
t.assert.strictEqual(isValidHeaderValue('in\rvalid'), false)
t.assert.strictEqual(isValidHeaderValue('\rinvalid'), false)
})
test('should return false for string containing LF', (t) => {
t.assert.strictEqual(isValidHeaderValue('invalid\n'), false)
t.assert.strictEqual(isValidHeaderValue('in\nvalid'), false)
t.assert.strictEqual(isValidHeaderValue('\ninvalid'), false)
})
test('should return false for string with leading TAB', (t) => {
t.assert.strictEqual(isValidHeaderValue('\tinvalid'), false)
})
test('should return false for string with trailing TAB', (t) => {
t.assert.strictEqual(isValidHeaderValue('invalid\t'), false)
})
test('should return false for string with leading SPACE', (t) => {
t.assert.strictEqual(isValidHeaderValue(' invalid'), false)
})
test('should return false for string with trailing SPACE', (t) => {
t.assert.strictEqual(isValidHeaderValue('invalid '), false)
})
})
describe('isOriginIPPotentiallyTrustworthy()', () => {
[
['', false],
['0000:0000:0000:0000:0000:0000:0000:0001', true],
['0001:0000:0000:0000:0000:0000:0000:0001', false],
['0000:0000:0000:0000:0000:0000::0001', true],
['0001:0000:0000:0000:0000:0000::0001', false],
['0000:0000:0001:0000:0000:0000::0001', false],
['0000:0000:0000:0000:0000::0001', true],
['0000:0000:0000:0000::0001', true],
['0000:0000:0000::0001', true],
['0000:0000::0001', true],
['0000::0001', true],
['::1001', false],
['::0001', true],
['::0011', false],
['::1', true],
['[::1]', true],
['[::1', false],
['::1]', false],
['::2', false],
['::', false],
['126.0.0.0', false],
['127.0.0.0', true],
['127.0.0.1', true],
['127.255.255.255', true],
['128.255.255.255', false]
].forEach(([ip, expected]) => {
test(`${ip} is ${expected ? '' : 'not '}potentially trustworthy`, (t) => {
t.assert.strictEqual(util.isOriginIPPotentiallyTrustworthy(ip), expected)
})
})
})