js_repl: canonicalize paths for node_modules boundary checks (#12177)

## 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
This commit is contained in:
Curtis 'Fjord' Hawthorne
2026-02-18 11:56:45 -08:00
committed by GitHub
parent 82d82d9ca5
commit cc248e4681

View File

@@ -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)
);