🎩 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
2.4.2
to
2.4.3
+3
-0
.github/workflows/ci.yml

@@ -20,4 +20,7 @@ name: CI

test:
permissions:
contents: write
pull-requests: write
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
with:
license-check: true

@@ -184,2 +184,6 @@ 'use strict'

// Captures the authority component (between "//" and the next "/", "?" or "#"),
// with or without a scheme prefix, for the literal-backslash rejection below.
const AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/
function getParseError (parsed, matches) {

@@ -213,2 +217,15 @@ if (matches[2] !== undefined && parsed.path && parsed.path[0] !== '/') {

// A literal backslash (U+005C) is not a valid RFC 3986 URI character and is
// not an authority delimiter. Reject it in the authority rather than
// rewriting it: normalizing "\" -> "/" (WHATWG error recovery) could silently
// change the resource identified by an otherwise-invalid input, and lets "\"
// act as a host delimiter here while Node's native URL parses a different
// host (SSRF / redirect / origin-allowlist bypass). Percent-encoded %5C is
// untouched and remains valid encoded data.
const authorityMatch = uri.match(AUTHORITY_PREFIX)
if (authorityMatch !== null && authorityMatch[1].indexOf('\\') !== -1) {
parsed.error = 'URI authority must not contain a literal backslash.'
malformedAuthorityOrPort = true
}
const matches = uri.match(URI_PARSE)

@@ -215,0 +232,0 @@

+1
-1
{
"name": "fast-uri",
"description": "Dependency free RFC 3986 URI toolbox",
"version": "2.4.2",
"version": "2.4.3",
"main": "index.js",

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

@@ -163,1 +163,63 @@ 'use strict'

})
test('parse rejects a literal backslash in the authority as malformed (RFC 3986)', (t) => {
// Regression for the host-confusion bypass: a literal "\" is invalid RFC 3986
// syntax and must be flagged malformed, not silently rewritten. Otherwise "\"
// acts as a host delimiter here while Node's native URL parses a different
// host, defeating a host-based SSRF/redirect/origin allowlist.
const cases = [
'http://evil.com\\@allowed.com',
'https://169.254.169.254\\@trusted.example.com',
'http://127.0.0.1\\@public.example.com',
'https://attacker.com\\@api.internal',
'http://a\\@b',
'ws://evil.com\\@allowed.com/chat',
'wss://evil.com\\@allowed.com/chat',
'http://evil.com\\%40allowed.com',
'//evil.com\\@allowed.com'
]
t.plan(cases.length)
cases.forEach((input) => {
t.equal(
fastURI.parse(input).error,
'URI authority must not contain a literal backslash.',
input
)
})
})
test('normalize does not canonicalize a literal-backslash URI into a different valid URL', (t) => {
const cases = [
'http://evil.com\\@allowed.com',
'https://attacker.com\\@api.internal'
]
t.plan(cases.length)
cases.forEach((input) => {
t.equal(fastURI.normalize(input), input, input)
})
})
test('parse leaves percent-encoded %5C untouched as encoded data (not rejected)', (t) => {
// Only the literal "\" byte is rejected; %5C stays valid encoded data and
// does not diverge from the native URL parser, so it must not be flagged.
const input = 'http://evil.com%5C@allowed.com'
const parsed = fastURI.parse(input)
t.plan(2)
t.notOk(parsed.error, '%5C is valid encoded data, not malformed')
t.equal(parsed.host, new URL(input).hostname, '%5C host matches native URL (no divergence)')
})
test('parse does not reject a literal backslash in the query or fragment', (t) => {
// The rejection is scoped to the authority/path (the host-confusion surface);
// a backslash after "?"/"#" is normalized as encoded data as before.
const parsed = fastURI.parse('http://host.example.com/?x=\\y#z\\w')
t.plan(2)
t.notOk(parsed.error, 'backslash in query/fragment does not mark the URI malformed')
t.equal(parsed.host, 'host.example.com', 'host parsed normally')
})