Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/api/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ added:
* Returns: {net.SocketAddress} Returns a `SocketAddress` if parsing was successful.
Otherwise returns `undefined`.

The `input` may contain only hexadecimal digits, `x`, `.`, `:`, `[`, and `]`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would personally suggest something like

The address portion of `input` must be a valid IPv4 or IPv6 hostname as recognized by the [WHATWG URL parser][]`.

^ https://url.spec.whatwg.org/#host-parsing

Anything else returns `undefined`, including other URL components such as
`user@1.2.3.4` or `1.2.3.4/foo`, whitespace, control characters,
percent-encoding, and non-ASCII characters.

## Class: `net.Server`

<!-- YAML
Expand Down
10 changes: 10 additions & 0 deletions lib/internal/socketaddress.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const {
ObjectSetPrototypeOf,
RegExpPrototypeExec,
Symbol,
} = primordials;

Expand Down Expand Up @@ -42,6 +43,11 @@ const { URLParse } = require('internal/url');
const kHandle = Symbol('kHandle');
const kDetail = Symbol('kDetail');

// The complete character set of an "${address}:${port}" input. Everything else
// is rejected: delimiters that would introduce a userinfo, path, query, or
// fragment component, and characters the URL parser strips, decodes, or remaps.
const kValidInput = /^[0-9a-fA-FxX.:[\]]+$/;

class SocketAddress {
static isSocketAddress(value) {
return value?.[kHandle] !== undefined;
Expand Down Expand Up @@ -149,6 +155,10 @@ class SocketAddress {
*/
static parse(input) {
validateString(input, 'input');
// The URL parser below silently discards anything that is not the host or
// port, and percent-decodes and IDNA-maps what remains, so restrict the
// input to characters that can appear in an address and port first.
if (RegExpPrototypeExec(kValidInput, input) === null) return;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only downside is that this matches hostname-like strings like cabbage.ca, which will be parsed by URLParse and returned from this method as "IPv4 addresses".

Should probably also check after parsing that url.hostname passes isIPv4/isIPv6 as appropriate.

@Renegade334 Renegade334 Jul 30, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or indeed could just validate url.hostname with the simpler /^\[|\.\d+$/ since anything matching those that's not a valid IPv4/IPv6 address would already have been rejected by URLParse.

// While URL.parse is not expected to throw, there are several
// other pieces here that do... the destucturing, the SocketAddress
// constructor, etc. So we wrap this in a try/catch to be safe.
Expand Down
36 changes: 36 additions & 0 deletions test/parallel/test-socketaddress.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,42 @@ describe('net.SocketAddress...', () => {
'abc.123',
'259.1.1.1',
'12:12:12',
// Arbitrary URL components.
'user:80@5.6.7.8',
'user@1.2.3.4',
'1.2.3.4/',
'1.2.3.4/foo',
'1.2.3.4\\foo',
'1.2.3.4?a=b',
'1.2.3.4#frag',
'[1::8]:123/x',
'http://1.2.3.4',
// Whitespace and control characters.
'1.2.3\n.4',
'1.2.3\t.4',
'1.2.3.4\r',
'1.2.3.4 ',
' 1.2.3.4',
'1.2.3.4\x00',
'1.2.3.4\x0b',
'1.2.3.4\x7f',
// Percent-encoding.
'1.2.3.%34',
'1%2E2%2E3%2E4',
'%30%78%66%66%66%66%66%66%66%66',
'1.2.3.%34:8080',
// IDNA.
'127.0.0.1',
'0x7f.1',
'1。2。3。4',
'1。2。3。4',
'⑧.0.0.1',
'1.2.3.4\u200b',
'1.2.3.4\ufeff',
'1.2.3.4\u00ad',
'1.2.3.4\u180e',
'1.2.3.4\u2064',
'1.2.3.4\ufe00',
];

bad.forEach((i) => {
Expand Down
Loading