Access control / Permissions (Authorization)

HIGH
nodejs/node
Commit: 1d2686d15e78
Affected: < 25.9.0
2026-04-05 11:11 UTC

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')); +}
← Back to Alerts View on GitHub →