From cc248e4681745a4ddeab9c2cf7a81720af6e9495 Mon Sep 17 00:00:00 2001 From: Curtis 'Fjord' Hawthorne Date: Wed, 18 Feb 2026 11:56:45 -0800 Subject: [PATCH] js_repl: canonicalize paths for node_modules boundary checks (#12177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fix `js_repl` package-resolution boundary checks for macOS temp directory path aliasing (`/var` vs `/private/var`). ## Problem `js_repl` verifies that resolved bare-package imports stay inside a configured `node_modules` root. On macOS, temp directories are commonly exposed as `/var/...` but canonicalize to `/private/var/...`. Because the boundary check compared raw paths with `path.relative(...)`, valid resolutions under temp dirs could be misclassified as escaping the allowed base, causing false `Module not found` errors. ## Changes - Add `fs` import in the JS kernel. - Add `canonicalizePath()` using `fs.realpathSync.native(...)` (with safe fallback). - Canonicalize both `base` and `resolvedPath` before running the `node_modules` containment check. ## Impact - Fixes false-negative boundary checks for valid package resolutions in macOS temp-dir scenarios. - Keeps the existing security boundary behavior intact. - Scope is limited to `js_repl` kernel module path validation logic. #### [git stack](https://github.com/magus/git-stack-cli) - 👉 `1` https://github.com/openai/codex/pull/12177 - ⏳ `2` https://github.com/openai/codex/pull/10673 --- codex-rs/core/src/tools/js_repl/kernel.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/codex-rs/core/src/tools/js_repl/kernel.js b/codex-rs/core/src/tools/js_repl/kernel.js index bcc9d1e36..67a1f23a8 100644 --- a/codex-rs/core/src/tools/js_repl/kernel.js +++ b/codex-rs/core/src/tools/js_repl/kernel.js @@ -4,6 +4,7 @@ const { Buffer } = require("node:buffer"); const crypto = require("node:crypto"); +const fs = require("node:fs"); const { builtinModules, createRequire } = require("node:module"); const { createInterface } = require("node:readline"); const { performance } = require("node:perf_hooks"); @@ -149,6 +150,14 @@ const moduleSearchBases = (() => { const importResolveConditions = new Set(["node", "import"]); const requireByBase = new Map(); +function canonicalizePath(value) { + try { + return fs.realpathSync.native(value); + } catch { + return value; + } +} + function getRequireForBase(base) { let req = requireByBase.get(base); if (!req) { @@ -165,8 +174,10 @@ function isModuleNotFoundError(err) { } function isWithinBaseNodeModules(base, resolvedPath) { - const nodeModulesRoot = path.resolve(base, "node_modules"); - const relative = path.relative(nodeModulesRoot, resolvedPath); + const canonicalBase = canonicalizePath(base); + const canonicalResolved = canonicalizePath(resolvedPath); + const nodeModulesRoot = path.resolve(canonicalBase, "node_modules"); + const relative = path.relative(nodeModulesRoot, canonicalResolved); return ( relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative) );