Access control / Permissions (Authorization)
Description
The commit adds a network permission check in PipeWrap::Connect to enforce network-scoped authorization before initiating a pipe connect (PermissionScope::kNet). This change protects IPC/pipe connection paths from being accessed by untrusted code lacking net permissions, addressing an access-control vulnerability identified as CVE-2026-21636. Prior to this change, pipe_wrap.connect could be invoked without verifying network permissions, potentially allowing unauthorized processes or modules to establish network-related IPC connections.
Proof of Concept
PoC (conceptual, to reproduce in a test environment that exposes Node's permission model):
1) Create a simple UNIX-domain socket server that represents a sensitive IPC endpoint.
2) Attempt to connect to that socket from a client within a script/process that does not have net permission.
3) Observe the behavior with and without the patch:
- Pre-fix (no net permission check): the client would successfully establish a connection to the IPC socket.
- Post-fix (net permission check present): the client should receive an error such as ERR_ACCESS_DENIED when the net permission is not granted.
Server (server.js):
const net = require('net');
const path = '/tmp/perm.sock';
const fs = require('fs');
if (fs.existsSync(path)) fs.unlinkSync(path);
const server = net.createServer((socket) => { socket.end('OK'); });
server.listen(path, () => console.log('listening', path));
Client (client.js):
const net = require('net');
const path = '/tmp/perm.sock';
const client = net.connect({ path }, () => { console.log('connected'); client.end(); });
client.on('error', (err) => { console.error('connect error', err.code); });
Test steps:
- Run node server.js to start the IPC endpoint.
- Run the client.js script in a Node.js environment that does not have net permission (or without enabling the net permission in the sandbox). The client should fail with ERR_ACCESS_DENIED due to the new permission check.
- If you grant net permission (via the appropriate sandbox/runtime flags) and re-run client.js, the connection should succeed (or at least not be blocked by ERR_ACCESS_DENIED).
Note: The exact command-line flag to enable/disable the net permission depends on the Node test harness in use (the patch references a test using --permission and --allow-fs-read etc.). Use the harness-provided mechanism to simulate granting/restricting permission for net operations in your environment.
Commit Details
Author: RafaelGSS
Date: 2025-12-27 23:36 UTC
Message:
permission: add network check on pipe_wrap connect
Refs: https://hackerone.com/reports/3465156
PR-URL: https://github.com/nodejs-private/node-private/pull/784
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
CVE-ID: CVE-2026-21636
Triage Assessment
Vulnerability Type: Access control / Permissions (Authorization) vulnerability
Confidence: HIGH
Reasoning:
The commit adds a permission check (network scope) before initiating a pipe connect, returning ERR_ACCESS_DENIED when permissions are insufficient. This directly addresses an access control vulnerability related to network operations in pipe_wrap.connect, aligning with the referenced CVE-2026-21636.
Verification Assessment
Vulnerability Type: Access control / Permissions (Authorization)
Confidence: HIGH
Affected Versions: < 25.9.0
Code Diff
diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc
index 2cb61215604047..7bd15d59031284 100644
--- a/src/pipe_wrap.cc
+++ b/src/pipe_wrap.cc
@@ -226,6 +226,9 @@ void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) {
Local<Object> req_wrap_obj = args[0].As<Object>();
node::Utf8Value name(env->isolate(), args[1]);
+ ERR_ACCESS_DENIED_IF_INSUFFICIENT_PERMISSIONS(
+ env, permission::PermissionScope::kNet, name.ToStringView(), args);
+
ConnectWrap* req_wrap =
new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_PIPECONNECTWRAP);
int err = req_wrap->Dispatch(uv_pipe_connect2,
diff --git a/test/parallel/test-permission-net-uds.js b/test/parallel/test-permission-net-uds.js
new file mode 100644
index 00000000000000..7024c9ff6d3b16
--- /dev/null
+++ b/test/parallel/test-permission-net-uds.js
@@ -0,0 +1,31 @@
+// Flags: --permission --allow-fs-read=*
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto) { common.skip('missing crypto'); };
+
+if (common.isWindows) {
+ common.skip('This test only works on unix');
+}
+
+const assert = require('assert');
+const net = require('net');
+const tls = require('tls');
+
+{
+ const client = net.connect({ path: '/tmp/perm.sock' });
+ client.on('error', common.mustCall((err) => {
+ assert.strictEqual(err.code, 'ERR_ACCESS_DENIED');
+ }));
+
+ client.on('connect', common.mustNotCall('TCP connection should be blocked'));
+}
+
+{
+ const client = tls.connect({ path: '/tmp/perm.sock' });
+ client.on('error', common.mustCall((err) => {
+ assert.strictEqual(err.code, 'ERR_ACCESS_DENIED');
+ }));
+
+ client.on('connect', common.mustNotCall('TCP connection should be blocked'));
+}