Skip to content

Commit c551a51

Browse files
mcollinaaduh95
authored andcommitted
dns,net: reject hostnames with embedded NUL bytes
Ref: https://hackerone.com/reports/3656716 PR-URL: nodejs-private/node-private#868 Reviewed-By: Antoine du Hamel <[email protected]> CVE-ID: CVE-2026-48930 Refs: https://hackerone.com/reports/3656716
1 parent 39d1d09 commit c551a51

6 files changed

Lines changed: 40 additions & 4 deletions

File tree

lib/dns.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ const {
8484
validateNumber,
8585
validateOneOf,
8686
validatePort,
87-
validateString,
87+
validateStringWithoutNullBytes,
8888
} = require('internal/validators');
8989

9090
const {
@@ -148,7 +148,7 @@ function lookup(hostname, options, callback) {
148148

149149
// Parse arguments
150150
if (hostname) {
151-
validateString(hostname, 'hostname');
151+
validateStringWithoutNullBytes(hostname, 'hostname');
152152
}
153153

154154
if (typeof options === 'function') {

lib/internal/dns/promises.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const {
7171
validateOneOf,
7272
validatePort,
7373
validateString,
74+
validateStringWithoutNullBytes,
7475
} = require('internal/validators');
7576

7677
const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
@@ -201,7 +202,7 @@ function lookup(hostname, options) {
201202

202203
// Parse arguments
203204
if (hostname) {
204-
validateString(hostname, 'hostname');
205+
validateStringWithoutNullBytes(hostname, 'hostname');
205206
}
206207

207208
if (typeof options === 'number') {

lib/internal/validators.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const {
1616
ObjectPrototypeHasOwnProperty,
1717
RegExpPrototypeExec,
1818
String,
19+
StringPrototypeIncludes,
1920
StringPrototypeToUpperCase,
2021
StringPrototypeTrim,
2122
} = primordials;
@@ -164,6 +165,14 @@ const validateString = hideStackFrames((value, name) => {
164165
throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
165166
});
166167

168+
/** @type {validateString} */
169+
const validateStringWithoutNullBytes = hideStackFrames((value, name) => {
170+
validateString(value, name);
171+
if (StringPrototypeIncludes(value, '\u0000')) {
172+
throw new ERR_INVALID_ARG_VALUE(name, value, 'must be a string without null bytes');
173+
}
174+
});
175+
167176
/**
168177
* @callback validateNumber
169178
* @param {*} value
@@ -643,6 +652,7 @@ module.exports = {
643652
validatePort,
644653
validateSignalName,
645654
validateString,
655+
validateStringWithoutNullBytes,
646656
validateUint32,
647657
validateUndefined,
648658
validateUnion,

lib/net.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ const {
131131
validateNumber,
132132
validatePort,
133133
validateString,
134+
validateStringWithoutNullBytes,
134135
} = require('internal/validators');
135136
const kLastWriteQueueSize = Symbol('lastWriteQueueSize');
136137
const { getOptionValue } = require('internal/options');
@@ -1312,7 +1313,7 @@ function lookupAndConnect(self, options) {
13121313
const host = options.host || 'localhost';
13131314
let { port, autoSelectFamilyAttemptTimeout, autoSelectFamily } = options;
13141315

1315-
validateString(host, 'options.host');
1316+
validateStringWithoutNullBytes(host, 'options.host');
13161317

13171318
if (localAddress && !isIP(localAddress)) {
13181319
throw new ERR_INVALID_IP_ADDRESS(localAddress);

test/parallel/test-dns-lookup.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ const dnsPromises = dns.promises;
2323
assert.throws(() => dnsPromises.lookup(1, {}), err);
2424
}
2525

26+
{
27+
const err = {
28+
code: 'ERR_INVALID_ARG_VALUE',
29+
name: 'TypeError',
30+
message: /The argument 'hostname' must be a string without null bytes\./,
31+
};
32+
33+
assert.throws(() => dns.lookup('127.0.0.1\u0000.allowed.example', {}), err);
34+
assert.throws(() => dnsPromises.lookup('127.0.0.1\u0000.allowed.example', {}), err);
35+
}
36+
2637
// This also verifies different expectWarning notations.
2738
common.expectWarning({
2839
// For 'internal/test/binding' module.

test/parallel/test-net-connect-options-invalid.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,16 @@ const net = require('net');
3737
name: 'TypeError',
3838
});
3939
}
40+
41+
{
42+
assert.throws(() => {
43+
net.createConnection({
44+
host: '127.0.0.1\u0000.allowed.example',
45+
port: 8080,
46+
});
47+
}, {
48+
code: 'ERR_INVALID_ARG_VALUE',
49+
name: 'TypeError',
50+
message: /The property 'options\.host' must be a string without null bytes\./,
51+
});
52+
}

0 commit comments

Comments
 (0)