Commit a3709662825 for nodejs

commit a37096628256c0d4b4abbdf548e4d5e339e4f04f
Author: James M Snell <jasnell@gmail.com>
Date:   Fri Sep 18 21:58:43 2026 +0000

    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

diff --git a/lib/internal/dns/callback_resolver.js b/lib/internal/dns/callback_resolver.js
index f634f452e91..68822ea291b 100644
--- a/lib/internal/dns/callback_resolver.js
+++ b/lib/internal/dns/callback_resolver.js
@@ -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();
diff --git a/lib/internal/dns/promises.js b/lib/internal/dns/promises.js
index 1dd6417d753..aaa8a03be76 100644
--- a/lib/internal/dns/promises.js
+++ b/lib/internal/dns/promises.js
@@ -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);
diff --git a/lib/internal/dns/utils.js b/lib/internal/dns/utils.js
index 50ec10d3b04..dcc2828b0f9 100644
--- a/lib/internal/dns/utils.js
+++ b/lib/internal/dns/utils.js
@@ -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);
diff --git a/test/parallel/test-dns-resolve-null-bytes.js b/test/parallel/test-dns-resolve-null-bytes.js
new file mode 100644
index 00000000000..ef091d06a0d
--- /dev/null
+++ b/test/parallel/test-dns-resolve-null-bytes.js
@@ -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);
+}
diff --git a/test/parallel/test-dns-setlocaladdress.js b/test/parallel/test-dns-setlocaladdress.js
index 25bece328f4..b47916115a2 100644
--- a/test/parallel/test-dns-setlocaladdress.js
+++ b/test/parallel/test-dns-setlocaladdress.js
@@ -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 /,
+    });
+  }
+}