dns: validate resolve inputs for null bytes

Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-by: Opencode
PR-URL: https://github.com/nodejs-private/node-private/pull/984
Fixes: https://hackerone.com/reports/4038342
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
CVE-ID: CVE-2026-105482
This commit is contained in:
James M Snell authored and Antoine du Hamel committed 2026-10-21 12:57:35 +02:00
1 parent b85dd8021c
commit a370966282
5 files changed
+102 -5

No files matched your search

+2 -2
View File
@@ -20,7 +20,7 @@ const {
const {
validateFunction,
validateString,
validateStringWithoutNullBytes,
} = require('internal/validators');
const {
@@ -59,7 +59,7 @@ function resolver(bindingName) {
callback = arguments[2];
}
validateString(name, 'name');
validateStringWithoutNullBytes(name, 'name');
validateFunction(callback, 'callback');
const req = new QueryReqWrap();
+1 -1
View File
@@ -339,7 +339,7 @@ function createResolverPromise(resolver, bindingName, hostname, ttl) {
function resolver(bindingName) {
function query(name, options) {
validateString(name, 'name');
validateStringWithoutNullBytes(name, 'name');
const ttl = !!(options?.ttl);
return createResolverPromise(this, bindingName, name, ttl);
+3 -2
View File
@@ -26,6 +26,7 @@ const {
validateOneOf,
validatePort,
validateString,
validateStringWithoutNullBytes,
validateUint32,
} = require('internal/validators');
let binding;
@@ -178,10 +179,10 @@ class ResolverBase {
setLocalAddress(ipv4, ipv6) {
validateString(ipv4, 'ipv4');
validateStringWithoutNullBytes(ipv4, 'ipv4');
if (ipv6 !== undefined) {
validateString(ipv6, 'ipv6');
validateStringWithoutNullBytes(ipv6, 'ipv6');
}
this._handle.setLocalAddress(ipv4, ipv6);
@@ -0,0 +1,75 @@
'use strict';
const common = require('../common');
// Names containing null bytes must be rejected by the resolver methods.
// c-ares receives the name as a C string, so everything after the first
// null byte would otherwise be silently dropped, e.g. resolving
// 'example.com\0.trusted.corp' would actually query 'example.com'.
const assert = require('assert');
const dns = require('dns');
const dnsPromises = dns.promises;
const name = 'example.com\u0000.trusted.corp';
const ip = '127.0.0.1\u0000.trusted.corp';
const err = {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: /^The argument 'name' must be a string without null bytes\. Received /,
};
const methods = [
'resolve',
'resolve4',
'resolve6',
'resolveAny',
'resolveCaa',
'resolveCname',
'resolveMx',
'resolveNaptr',
'resolveNs',
'resolvePtr',
'resolveSoa',
'resolveSrv',
'resolveTlsa',
'resolveTxt',
];
const rrtypes = [
'A',
'AAAA',
'ANY',
'CAA',
'CNAME',
'MX',
'NAPTR',
'NS',
'PTR',
'SOA',
'SRV',
'TLSA',
'TXT',
];
// Callback-based API: the default resolver and a Resolver instance.
for (const resolver of [dns, new dns.Resolver()]) {
for (const method of methods) {
assert.throws(() => resolver[method](name, common.mustNotCall()), err);
}
for (const rrtype of rrtypes) {
assert.throws(() => resolver.resolve(name, rrtype, common.mustNotCall()), err);
}
assert.throws(() => resolver.reverse(ip, common.mustNotCall()), err);
}
// Promise-based API: the default resolver and a Resolver instance.
for (const resolver of [dnsPromises, new dnsPromises.Resolver()]) {
for (const method of methods) {
assert.throws(() => resolver[method](name), err);
}
for (const rrtype of rrtypes) {
assert.throws(() => resolver.resolve(name, rrtype), err);
}
assert.throws(() => resolver.reverse(ip), err);
}
+21
View File
@@ -38,3 +38,24 @@ const promiseResolver = new dns.promises.Resolver();
promiseResolver.setLocalAddress();
}, Error);
}
// Verify that setLocalAddress rejects addresses containing null bytes instead
// of silently truncating them at the first null byte
{
for (const r of [resolver, promiseResolver]) {
assert.throws(() => {
r.setLocalAddress('127.0.0.1\u0000junk');
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: /^The argument 'ipv4' must be a string without null bytes\. Received /,
});
assert.throws(() => {
r.setLocalAddress('127.0.0.1', '::1\u0000junk');
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
message: /^The argument 'ipv6' must be a string without null bytes\. Received /,
});
}
}