| 1 | 'use strict'
|
|---|
| 2 |
|
|---|
| 3 | const test = require('tape')
|
|---|
| 4 | const fastURI = require('..')
|
|---|
| 5 |
|
|---|
| 6 | test('parse marks malformed authority and port inputs as errors', (t) => {
|
|---|
| 7 | const malformedCases = [
|
|---|
| 8 | {
|
|---|
| 9 | input: 'http://[::1]foo',
|
|---|
| 10 | expectedError: 'URI path must start with "/" when authority is present.'
|
|---|
| 11 | },
|
|---|
| 12 | {
|
|---|
| 13 | input: 'http://[::1]:80abc/path',
|
|---|
| 14 | expectedError: 'URI path must start with "/" when authority is present.'
|
|---|
| 15 | },
|
|---|
| 16 | {
|
|---|
| 17 | input: 'http://example.com:80abc/path',
|
|---|
| 18 | expectedError: 'URI path must start with "/" when authority is present.'
|
|---|
| 19 | },
|
|---|
| 20 | {
|
|---|
| 21 | input: 'http://[::1]:65536',
|
|---|
| 22 | expectedError: 'URI port is malformed.'
|
|---|
| 23 | }
|
|---|
| 24 | ]
|
|---|
| 25 |
|
|---|
| 26 | t.plan(malformedCases.length)
|
|---|
| 27 |
|
|---|
| 28 | malformedCases.forEach(({ input, expectedError }) => {
|
|---|
| 29 | t.equal(fastURI.parse(input).error, expectedError, input)
|
|---|
| 30 | })
|
|---|
| 31 | })
|
|---|
| 32 |
|
|---|
| 33 | test('normalize does not canonicalize malformed URLs into different valid URLs', (t) => {
|
|---|
| 34 | const malformedCases = [
|
|---|
| 35 | 'http://[::1]foo',
|
|---|
| 36 | 'http://[::1]:80abc/path',
|
|---|
| 37 | 'http://example.com:80abc/path',
|
|---|
| 38 | 'http://[::1]:65536'
|
|---|
| 39 | ]
|
|---|
| 40 |
|
|---|
| 41 | t.plan(malformedCases.length)
|
|---|
| 42 |
|
|---|
| 43 | malformedCases.forEach((input) => {
|
|---|
| 44 | t.equal(fastURI.normalize(input), input, input)
|
|---|
| 45 | })
|
|---|
| 46 | })
|
|---|
| 47 |
|
|---|
| 48 | test('equal returns false when either side is malformed', (t) => {
|
|---|
| 49 | const malformedPairs = [
|
|---|
| 50 | ['http://[::1]foo', 'http://[::1]/foo'],
|
|---|
| 51 | ['http://[::1]:80abc/path', 'http://[::1]/abc/path'],
|
|---|
| 52 | ['http://example.com:80abc/path', 'http://example.com/abc/path'],
|
|---|
| 53 | ['http://[::1]:65536', 'http://[::1]:65536/']
|
|---|
| 54 | ]
|
|---|
| 55 |
|
|---|
| 56 | t.plan(malformedPairs.length)
|
|---|
| 57 |
|
|---|
| 58 | malformedPairs.forEach(([left, right]) => {
|
|---|
| 59 | t.equal(fastURI.equal(left, right), false, `${left} != ${right}`)
|
|---|
| 60 | })
|
|---|
| 61 | })
|
|---|
| 62 |
|
|---|
| 63 | test('normalize preserves encoded authority delimiters in host', (t) => {
|
|---|
| 64 | const cases = [
|
|---|
| 65 | ['http://trusted.com%40evil.com/', 'http://trusted.com%40evil.com/'],
|
|---|
| 66 | ['http://example.com%3A8080/', 'http://example.com%3A8080/'],
|
|---|
| 67 | ['http://example.com%2Fevil.com/path', 'http://example.com%2Fevil.com/path'],
|
|---|
| 68 | ['http://example.com%23fragment/path', 'http://example.com%23fragment/path'],
|
|---|
| 69 | ['http://example.com%3Fq=evil/path', 'http://example.com%3Fq=evil/path'],
|
|---|
| 70 | ['http://user%3Apass%40evil.com/', 'http://user%3Apass%40evil.com/'],
|
|---|
| 71 | ['http://user@trusted.com%40evil.com/', 'http://user@trusted.com%40evil.com/'],
|
|---|
| 72 | ['https://trusted.com%40evil.com/', 'https://trusted.com%40evil.com/'],
|
|---|
| 73 | ['ws://trusted.com%40evil.com/chat', 'ws://trusted.com%40evil.com/chat'],
|
|---|
| 74 | ['wss://trusted.com%40evil.com/chat', 'wss://trusted.com%40evil.com/chat']
|
|---|
| 75 | ]
|
|---|
| 76 |
|
|---|
| 77 | t.plan(cases.length)
|
|---|
| 78 |
|
|---|
| 79 | cases.forEach(([input, expected]) => {
|
|---|
| 80 | t.equal(fastURI.normalize(input), expected, input)
|
|---|
| 81 | })
|
|---|
| 82 | })
|
|---|
| 83 |
|
|---|
| 84 | test('parse preserves encoded authority delimiters in host', (t) => {
|
|---|
| 85 | const cases = [
|
|---|
| 86 | ['http://trusted.com%40evil.com/', 'trusted.com%40evil.com'],
|
|---|
| 87 | ['http://example.com%3A8080/', 'example.com%3A8080'],
|
|---|
| 88 | ['http://user%3Apass%40evil.com/', 'user%3Apass%40evil.com']
|
|---|
| 89 | ]
|
|---|
| 90 |
|
|---|
| 91 | t.plan(cases.length)
|
|---|
| 92 |
|
|---|
| 93 | cases.forEach(([input, expectedHost]) => {
|
|---|
| 94 | t.equal(fastURI.parse(input).host, expectedHost, input)
|
|---|
| 95 | })
|
|---|
| 96 | })
|
|---|
| 97 |
|
|---|
| 98 | test('equal returns false when encoded delimiters differ from live delimiters', (t) => {
|
|---|
| 99 | const pairs = [
|
|---|
| 100 | ['http://trusted.com%40evil.com/', 'http://trusted.com@evil.com/'],
|
|---|
| 101 | ['http://example.com%3A8080/', 'http://example.com:8080/']
|
|---|
| 102 | ]
|
|---|
| 103 |
|
|---|
| 104 | t.plan(pairs.length)
|
|---|
| 105 |
|
|---|
| 106 | pairs.forEach(([left, right]) => {
|
|---|
| 107 | t.equal(fastURI.equal(left, right, {}), false, `${left} != ${right}`)
|
|---|
| 108 | })
|
|---|
| 109 | })
|
|---|
| 110 |
|
|---|
| 111 | test('resolve preserves encoded authority delimiters', (t) => {
|
|---|
| 112 | const result = fastURI.resolve('http://base.com/', '//trusted.com%40evil.com/path')
|
|---|
| 113 | const parsed = fastURI.parse(result)
|
|---|
| 114 |
|
|---|
| 115 | t.plan(1)
|
|---|
| 116 | t.notEqual(parsed.host, 'evil.com', '//trusted.com%40evil.com/path')
|
|---|
| 117 | })
|
|---|
| 118 |
|
|---|
| 119 | test('serialize escapes authority delimiters in host field', (t) => {
|
|---|
| 120 | const result = fastURI.serialize({ scheme: 'http', host: 'trusted.com@evil.com', path: '/' })
|
|---|
| 121 | const parsed = fastURI.parse(result)
|
|---|
| 122 |
|
|---|
| 123 | t.plan(1)
|
|---|
| 124 | t.notEqual(parsed.host, 'evil.com', 'host: trusted.com@evil.com')
|
|---|
| 125 | })
|
|---|
| 126 |
|
|---|
| 127 | test('normalize does not double-decode %2540 into a live @', (t) => {
|
|---|
| 128 | const result = fastURI.normalize('http://trusted.com%2540evil.com/')
|
|---|
| 129 | const parsed = fastURI.parse(result)
|
|---|
| 130 |
|
|---|
| 131 | t.plan(1)
|
|---|
| 132 | t.notEqual(parsed.host, 'trusted.com@evil.com', 'http://trusted.com%2540evil.com/')
|
|---|
| 133 | })
|
|---|