'use strict' const test = require('tape') const fastURI = require('..') const MALFORMED_SCHEME_ERROR = 'URI scheme is malformed.' const malformedSchemes = [ '%2f%2fevil.example:/pwn', '%u002f%u002fevil.example:/pwn', '%0d%0aSet-Cookie:%20sid=attacker:/p', 'foo%3Abar:value', 'foo%2Fbar:value', '1http://example.com/', 'foo_bar:value', 'éxample:value', 'Kttp://example.com/', 'ſcheme:value' ] test('parse validates the decoded scheme against RFC 3986', (t) => { const validSchemes = [ ['a:value', 'a'], ['HTTP://example.com/', 'http'], ['a1+.-:value', 'a1+.-'], ['%4Aavascript:alert(1)', 'javascript'], ['foo%2Bbar:value', 'foo+bar'], ['%u006Aavascript:1', 'javascript'], ['ht%74ps://example.com/', 'https'] ] for (const [uri, scheme] of validSchemes) { const parsed = fastURI.parse(uri) t.equal(parsed.error, undefined, uri) t.equal(parsed.scheme, scheme, uri + ' scheme') } for (const uri of malformedSchemes) { const parsed = fastURI.parse(uri) t.equal(parsed.error, MALFORMED_SCHEME_ERROR, uri) } t.end() }) test('decoded schemes select their scheme handlers', (t) => { t.equal( fastURI.normalize('ht%74ps://example.com:443'), 'https://example.com/', 'HTTP normalization runs after decoding the scheme' ) // 3.x has no mailto scheme handler (added in v4); use the ws handler to // verify that a percent-encoded scheme still selects its scheme handler const ws = fastURI.parse('we%62socket://example.com/') t.equal(ws.scheme, 'websocket', 'ws parsing runs after decoding the scheme') t.end() }) test('normalize preserves schemes that decode to invalid identifiers', (t) => { for (const uri of malformedSchemes) { t.equal(fastURI.normalize(uri), uri, uri) } t.end() }) test('scheme normalization cannot introduce authority or control delimiters', (t) => { const authority = '%2f%2fevil.example:/pwn' const crlf = '%0d%0aSet-Cookie:%20sid=attacker:/p' t.equal(fastURI.parse(authority).host, undefined, 'original input has no authority') t.equal(fastURI.normalize(authority), authority, 'normalization does not create an authority') t.equal(fastURI.normalize(crlf), crlf, 'normalization does not emit raw CRLF') t.equal(fastURI.normalize(crlf).includes('\r\n'), false, 'normalized output contains no raw CRLF') t.end() }) test('equal returns false for malformed decoded schemes', (t) => { for (const uri of malformedSchemes) { t.equal(fastURI.equal(uri, uri, {}), false, uri) } t.end() }) test('resolve rejects malformed decoded schemes in either input', (t) => { t.throws( () => fastURI.resolve('%2f%2fevil.example:/base', 'child'), /URI scheme is malformed\./, 'malformed base' ) t.throws( () => fastURI.resolve('https://allowed.example/app/', '%2f%2fevil.example:/pwn'), /URI scheme is malformed\./, 'malformed relative reference' ) t.end() }) test('serialize validates decoded component schemes', (t) => { t.equal( fastURI.serialize({ scheme: 'foo%2Bbar', path: 'value' }), 'foo+bar:value', 'valid decoded scheme is serialized' ) t.throws( () => fastURI.serialize({ scheme: '//evil.example', path: '/pwn' }), /URI scheme is malformed\./, 'raw invalid scheme' ) t.throws( () => fastURI.serialize({ scheme: '%2f%2fevil.example', path: '/pwn' }), /URI scheme is malformed\./, 'encoded invalid scheme' ) t.equal( fastURI.equal( { scheme: '%2f%2fevil.example', path: '/pwn' }, { scheme: '%2f%2fevil.example', path: '/pwn' }, {} ), false, 'equality fails closed for malformed component objects' ) t.end() })