@bybrave/url-parse2
v2.0.1
Published
Maintained fork of url-parse — small-footprint URL parser with the Unicode hostname SSRF differential (GHSA-9pv6-g64m-xhrq) fixed, ESM, and bundled TypeScript types
Maintainers
Readme
@bybrave/url-parse2
Maintained, drop-in fork of url-parse — a small-footprint URL parser that works across Node.js and browser environments.
The original has been unmaintained since 2022 (last release 1.5.10) while still pulling ~163M downloads/month. This fork fixes an unpatched hostname SSRF differential, closes the most-requested open issues, and ships ESM plus bundled TypeScript types (no more separate @types/url-parse).
npm install @bybrave/url-parse2const Url = require('@bybrave/url-parse2'); // CommonJS
import Url from '@bybrave/url-parse2'; // ESMThe API is identical to url-parse — same constructor, same properties, same .set() / .toString(). Existing code keeps working; the changes below are either security hardening or additive.
What's fixed
| Issue | Problem | Fix |
|---|---|---|
| #241 | SSRF / allowlist bypass. Unicode confusable full stops (。 U+3002, . U+FF0E, 。 U+FF61) were left intact in hostname, while browsers and native URL normalize them to .. A validator could be tricked into trusting the wrong host. Advisory GHSA-9pv6-g64m-xhrq. | hostname/host are normalized to an ASCII dot, matching the platform URL. |
| #239 | Prototype-pollution surface: a crafted location object could inject __proto__ / constructor / prototype. | Reserved keys are never copied from a location object. |
| #38 | origin defaulted to the truthy string "null" for empty / relative URLs, inconsistent with every other empty field. | Empty / relative input now yields origin === ''. Opaque origins (a parsed non-special scheme such as foo://bar) still serialize to "null", matching native URL. |
| #132 | No way to keep a port that equals the protocol default (:80 for http:). | New keepDefaultPort option (see below). |
| #162 | query existed but searchParams (per MDN) did not. | New searchParams getter returning a URLSearchParams. |
| #168 | No ESM entry, no bundled types. | Ships import/require via an exports map and a bundled index.d.ts. |
New API
keepDefaultPort option
The fourth constructor argument accepts an options object. With keepDefaultPort: true, a port equal to the protocol default is preserved instead of stripped:
new Url('http://a.com:80').port; // '' (default)
new Url('http://a.com:80', null, false, { keepDefaultPort: true }).port; // '80'The flag is stored as a non-enumerable property, so it does not change the shape of the parsed object or its JSON serialization, and .set('port', …) respects it too.
searchParams
A URLSearchParams view over the parsed query, mirroring URL.searchParams:
const url = new Url('https://a.com/?x=1&y=2&x=3');
url.searchParams.get('x'); // '1'
url.searchParams.getAll('x'); // ['1', '3']It is computed on access from the current query. Mutating the returned object does not write back to the URL — assign url.query or use url.set('query', …) to change the query.
Migration from url-parse
Replace the dependency and the import — the parser behaves the same, with two intentional behavior changes to be aware of on a major bump:
originis now''(not'null') for empty/relative input. If you relied on the truthy'null', check for a falsy origin instead.hostnamenormalizes Unicode confusable dots. If you deliberately depended on the raw character surviving, that no longer holds (it was a security bug).
Everything else — properties, .set(), .toString(), the query parser (querystringify) — is unchanged.
Support
If this package saves you time, you can support maintenance:
Bitcoin (BTC): bc1q37557q5jpeaxqydzwvf3jgj7zhnfpn2td3q40q
Credits & license
MIT, same as the original — see LICENSE. Based on url-parse by Arnout Kazemier and contributors.
