reserved-path-normalization.test.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. 'use strict'
  2. const test = require('tape')
  3. const fastURI = require('..')
  4. const PATH_RESERVED = "!$&'()*+,;=:@/"
  5. function percentEncode (character, lowerCase) {
  6. const hex = character.charCodeAt(0).toString(16).padStart(2, '0')
  7. return '%' + (lowerCase ? hex : hex.toUpperCase())
  8. }
  9. test('normalize preserves literal and escaped reserved path characters', (t) => {
  10. t.equal(
  11. fastURI.normalize('http://example.com/a;b'),
  12. 'http://example.com/a;b',
  13. 'literal semicolon remains literal'
  14. )
  15. t.equal(
  16. fastURI.normalize('http://example.com/a%3ab'),
  17. 'http://example.com/a%3Ab',
  18. 'escaped colon remains escaped and its hex is uppercased'
  19. )
  20. for (const character of PATH_RESERVED) {
  21. const literal = `http://example.com/a${character}b`
  22. const escaped = `http://example.com/a${percentEncode(character, true)}b`
  23. const normalizedEscape = `http://example.com/a${percentEncode(character, false)}b`
  24. t.equal(fastURI.normalize(literal), literal, `preserves literal ${character}`)
  25. t.equal(fastURI.normalize(escaped), normalizedEscape, `preserves escaped ${character}`)
  26. }
  27. t.equal(
  28. fastURI.normalize('http://example.com/a/./café'),
  29. 'http://example.com/a/caf%C3%A9',
  30. 'removes real dot segments and UTF-8 encodes raw non-ASCII'
  31. )
  32. t.equal(
  33. fastURI.normalize('http://example.com/a/%2e%2e/b'),
  34. 'http://example.com/a/%2E%2E/b',
  35. 'preserves escaped dots as path data'
  36. )
  37. t.equal(
  38. fastURI.normalize('http://example.com/Kſ'),
  39. 'http://example.com/%E2%84%AA%C5%BF',
  40. 'UTF-8 encodes Unicode characters that case-fold to ASCII'
  41. )
  42. t.end()
  43. })
  44. test('serialize uses the RFC 3986 path character set without opening escapes', (t) => {
  45. const rawPath = `/a${PATH_RESERVED}b`
  46. t.equal(
  47. fastURI.serialize({ scheme: 'http', host: 'example.com', path: rawPath }),
  48. `http://example.com${rawPath}`,
  49. 'keeps all legal raw path characters'
  50. )
  51. const escapedPath = Array.from(PATH_RESERVED, (character) => percentEncode(character, true)).join('')
  52. const normalizedEscapedPath = Array.from(PATH_RESERVED, (character) => percentEncode(character, false)).join('')
  53. t.equal(
  54. fastURI.serialize({ scheme: 'http', host: 'example.com', path: '/' + escapedPath }),
  55. 'http://example.com/' + normalizedEscapedPath,
  56. 'uppercases valid escapes without decoding reserved characters'
  57. )
  58. t.equal(
  59. fastURI.serialize({ scheme: 'http', host: 'example.com', path: '/a?b#c[d]' }),
  60. 'http://example.com/a%3Fb%23c%5Bd%5D',
  61. 'encodes characters that would leave the path component'
  62. )
  63. t.equal(
  64. fastURI.serialize({ path: 'a:b/c:d' }),
  65. 'a%3Ab/c:d',
  66. 'only escapes a colon where path-noscheme requires it'
  67. )
  68. t.equal(
  69. fastURI.serialize({ path: './a:b/c:d' }),
  70. 'a%3Ab/c:d',
  71. 'escapes a first-segment colon exposed by dot-segment removal'
  72. )
  73. t.equal(
  74. fastURI.normalize('./a:b'),
  75. 'a%3Ab',
  76. 'normalization keeps a relative path from becoming a scheme'
  77. )
  78. t.equal(
  79. fastURI.serialize({ path: '/Kſ' }),
  80. '/%E2%84%AA%C5%BF',
  81. 'UTF-8 encodes Unicode characters that case-fold to ASCII'
  82. )
  83. t.end()
  84. })
  85. test('equal distinguishes escaped reserved path data from literal syntax', (t) => {
  86. for (const character of PATH_RESERVED) {
  87. const literal = `http://example.com/a${character}b`
  88. const escaped = `http://example.com/a${percentEncode(character, false)}b`
  89. t.equal(fastURI.equal(literal, escaped, {}), false, `distinguishes literal and escaped ${character}`)
  90. }
  91. t.equal(
  92. fastURI.equal('./a:b', 'a:b', {}),
  93. false,
  94. 'does not equate a relative path with an absolute URI after removing dot segments'
  95. )
  96. t.end()
  97. })