🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

fast-uri

Package Overview
Dependencies
Maintainers
11
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fast-uri - npm Package Compare versions

Comparing version
4.0.1
to
4.1.0
+33
test/query-fragment-normalization.test.js
'use strict'
const test = require('tape')
const fastURI = require('..')
test('query percent-encoding is normalized (RFC 3986 §6.2.2)', (t) => {
// hex digits are uppercased, like the path component already does
t.equal(fastURI.normalize('x://h/p?a=%2a'), 'x://h/p?a=%2A', 'uppercases hex in query')
// unreserved characters are decoded
t.equal(fastURI.normalize('x://h/p?a=%7e%2e'), 'x://h/p?a=~.', 'decodes unreserved in query')
// reserved characters stay percent-encoded
t.equal(fastURI.normalize('x://h/p?a=%2F'), 'x://h/p?a=%2F', 'keeps reserved encoded in query')
// raw characters that are not allowed are percent-encoded
t.equal(fastURI.normalize('x://h/p?a b'), 'x://h/p?a%20b', 'encodes a raw space in query')
t.equal(fastURI.normalize('x://h/p?café'), 'x://h/p?caf%C3%A9', 'encodes raw non-ASCII in query')
// `?` is allowed unencoded in a query
t.equal(fastURI.normalize('x://h/p?a?b'), 'x://h/p?a?b', 'keeps `?` raw in query')
t.end()
})
test('fragment percent-encoding is normalized without decoding reserved characters', (t) => {
// reserved characters must NOT be decoded — `%2F` is not `/`
t.equal(fastURI.normalize('x://h/p#a%2Fb%2A'), 'x://h/p#a%2Fb%2A', 'keeps reserved encoded in fragment')
// hex is uppercased and unreserved is decoded
t.equal(fastURI.normalize('x://h/p#a%2a%7e'), 'x://h/p#a%2A~', 'uppercases hex and decodes unreserved in fragment')
// raw characters are still encoded
t.equal(fastURI.normalize('x://h/p#a b'), 'x://h/p#a%20b', 'encodes a raw space in fragment')
// `?` and `/` are allowed unencoded in a fragment
t.equal(fastURI.normalize('x://h/p#f?x/y'), 'x://h/p#f?x/y', 'keeps `?` and `/` raw in fragment')
t.end()
})
+5
-6
'use strict'
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require('./lib/utils')
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, normalizeQueryFragmentEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require('./lib/utils')
const { SCHEMES, getSchemeHandler } = require('./lib/schemes')

@@ -328,8 +328,7 @@

}
if (parsed.query) {
parsed.query = normalizeQueryFragmentEncoding(parsed.query)
}
if (parsed.fragment) {
try {
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment))
} catch {
parsed.error = parsed.error || 'URI malformed'
}
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment)
}

@@ -336,0 +335,0 @@ }

@@ -18,2 +18,5 @@ 'use strict'

/** @type {(value: string) => boolean} */
const isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/?]$/iu)
/**

@@ -222,3 +225,3 @@ * @param {Array<string>} input

} else if (input[0] === '/') {
if (input[1] === '.' || input[1] === '/') {
if (input[1] === '.') {
output.push('/')

@@ -441,2 +444,59 @@ break

/**
* Normalizes the percent-encoding of a query or fragment component.
*
* Like `normalizePathEncoding`, but uses the query/fragment character set
* (which additionally allows `?`) and decodes `.` since it has no dot-segment
* meaning outside of a path.
*
* @param {string} input
* @returns {string}
*/
function normalizeQueryFragmentEncoding (input) {
let output = ''
for (let i = 0; i < input.length; i++) {
const ch = input[i]
if (ch === '%' && i + 2 < input.length) {
const hex = input.slice(i + 1, i + 3)
if (isHexPair(hex)) {
const normalizedHex = hex.toUpperCase()
const decoded = String.fromCharCode(parseInt(normalizedHex, 16))
if (isUnreserved(decoded)) {
output += decoded
} else {
output += '%' + normalizedHex
}
i += 2
continue
}
}
if (isQueryFragmentCharacter(ch)) {
output += ch
} else {
const code = input.charCodeAt(i)
if (code < 0x80) {
output += isEscapeSafe(code) ? ch : BYTE_HEX[code]
} else if (code < 0xD800 || code > 0xDFFF) {
output += percentEncodeNonAscii(code)
} else if (code <= 0xDBFF && i + 1 < input.length) {
const low = input.charCodeAt(i + 1)
if (low >= 0xDC00 && low <= 0xDFFF) {
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00))
i++
} else {
output += percentEncodeNonAscii(0xFFFD)
}
} else {
output += percentEncodeNonAscii(0xFFFD)
}
}
}
return output
}
/**
* Escapes a component while preserving existing valid percent escapes.

@@ -521,2 +581,3 @@ *

normalizePathEncoding,
normalizeQueryFragmentEncoding,
escapePreservingEscapes,

@@ -523,0 +584,0 @@ removeDotSegments,

{
"name": "fast-uri",
"description": "Dependency-free RFC 3986 URI toolbox",
"version": "4.0.1",
"version": "4.1.0",
"main": "index.js",

@@ -6,0 +6,0 @@ "type": "commonjs",

@@ -153,6 +153,8 @@ 'use strict'

// malformed percent-encoded fragment must not throw
// a fragment whose decoded bytes are not valid UTF-8 is still valid
// percent-encoding (RFC 3986 §2.1 is byte-level), so it is preserved as-is
// rather than being flagged as malformed
components = fastURI.parse('http://example.com/#%E0%A4A')
t.equal(components.error, 'URI malformed', 'malformed fragment errors')
t.equal(components.fragment, '%E0%A4A', 'malformed fragment is preserved')
t.equal(components.error, undefined, 'valid percent-encoding is not flagged as malformed')
t.equal(components.fragment, '%E0%A4A', 'fragment is preserved')

@@ -159,0 +161,0 @@ // all

@@ -32,2 +32,11 @@ 'use strict'

'WS:/WS://1305G130505:1&%0D:1&C(XXXXX*)))))))XXX130505:UUVUaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$aaaaaaaaaaaa13a'])
// trailing empty segments must be preserved: RFC 3986 5.2.4 only removes
// complete "." / ".." segments, never an empty segment
testCases.push(['//', '//'])
testCases.push(['/foo//', '/foo//'])
testCases.push(['/a/b//', '/a/b//'])
testCases.push(['/x//y//z', '/x//y//z'])
// "." / ".." segments still collapse
testCases.push(['/a/b/c/./../../g', '/a/g'])
testCases.push(['/.', '/'])

@@ -34,0 +43,0 @@ t.plan(testCases.length)