Skip to content

fix: native FS sandbox for ESM imports (#362)#2718

Open
Mivr wants to merge 6 commits intoaspect-build:mainfrom
Mivr:native-fs-sandbox
Open

fix: native FS sandbox for ESM imports (#362)#2718
Mivr wants to merge 6 commits intoaspect-build:mainfrom
Mivr:native-fs-sandbox

Conversation

@Mivr
Copy link
Collaborator

@Mivr Mivr commented Feb 7, 2026

Summary

  • Adds a native C shared library (fs_patch_linux.so / fs_patch_macos.dylib) loaded via LD_PRELOAD / DYLD_INSERT_LIBRARIES that intercepts libc realpath() to prevent ESM imports from escaping the Bazel sandbox
  • The core guarded_realpath algorithm resolves paths component-by-component, stopping at the first symlink hop that would escape the configured sandbox roots (execroot + runfiles)
  • Integrated into js_binary / js_test via the launcher template — automatically active when patch_node_fs = True (the default)

Problem

Issue #362: Node.js ESM resolver captures realpathSync.native() via destructuring before --require patches run:

// Node.js internal ESM loader (runs at module evaluation time)
const { realpathSync } = require('fs');  // captures BEFORE patches

This means the JS-level fs patches applied by register.cjs never intercept the ESM resolver's realpath calls, causing .mjs entry points and dynamic import() to resolve through Bazel sandbox symlinks to the real source tree.

Solution

Intercept realpath() at the C level via LD_PRELOAD — this runs before Node.js even starts, so the ESM resolver's captured reference is already patched.

Files

File Lines Purpose
fs_patch.h 118 Config struct, function declarations
fs_patch_init.c 152 __attribute__((constructor)) — env var parsing, dlsym
fs_patch_common.c 344 Core: guarded_realpath, is_sub_path, check_escape, normalize_path
fs_patch_linux.c 50 Linux interpositions: realpath, __realpath_chk, canonicalize_file_name
fs_patch_macos.c 35 macOS interpositions via __DATA,__interpose
js_binary.bzl +7 _fs_patch_native attr
js_binary.sh.tpl +33 LD_PRELOAD / DYLD_INSERT_LIBRARIES setup

Algorithm

  1. Fast path: Call real realpath(), check if the result escapes any sandbox root → if not, return it
  2. Slow path (escape detected): Walk path component-by-component with lstat + readlink, stop at the first symlink hop that escapes → return the guarded (non-escaped) path

Test plan

  • 2 C unit test suites (is_sub_path, check_escape, normalize_path, make_absolute, guarded_realpath) — bazel test //js/private/fs_patches_native/test:all
  • E2E test at e2e/esm_sandbox/.mjs entry point verifying realpathSync.native() stays within sandbox roots
  • All 122 existing //js/... tests pass with zero regressions
  • Golden launcher file updated

🤖 Generated with Claude Code

…dbox (aspect-build#362)

Node.js ESM resolver captures `realpathSync.native()` via destructuring
before `--require` patches can intercept it, causing resolved paths to
escape the Bazel sandbox. This adds a native C shared library loaded via
LD_PRELOAD (Linux) / DYLD_INSERT_LIBRARIES (macOS) that intercepts libc
`realpath()` at the C level — before Node.js even starts — stopping
symlink resolution at the first hop that would escape the sandbox roots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aspect-workflows
Copy link

aspect-workflows bot commented Feb 7, 2026

Bazel 7 (Test)

⚠️ Buildkite build #12170 failed.

//npm/private/test:__rollup-target failed to build

__rollup-target__js_binary failed: error executing Rollup command (from target //npm/private/test:__rollup-target) bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary empty.js --format cjs --file foo.js
 
Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
 
bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary: line 616:    11 Aborted                 (core dumped) "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} 0<&0 >> "$STDOUT_CAPTURE" 2>> "$STDERR_CAPTURE"
 
empty.jsfoo.js...
 
<--- Last few GCs --->
 
[11:0x2c925000]   294654 ms: Scavenge (interleaved) 2045.1 (2083.2) -> 2043.8 (2083.5) MB, pooled: 0 MB, 6.94 / 0.00 ms  (average mu = 0.249, current mu = 0.224) task;
[11:0x2c925000]   296148 ms: Mark-Compact (reduce) 2043.8 (2083.5) -> 2043.8 (2080.7) MB, pooled: 0 MB, 1280.84 / 0.00 ms  (+ 1.2 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 1287 ms) (average mu = 0.229, curr
 
<--- JS stacktrace --->
 
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
 
 1: 0xe40d24 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/587/execroot/_main/bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary.runfiles/_main/../rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/bin/node]
 2: 0x1216be0 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/587/execroot/_main/bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary.runfiles/_main/../rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/bin/node]
 3: 0x1216eb7 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/587/execroot/_main/bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js

... 1553 chars truncated
Failed tests (5)
//js/private/test/image/non_ascii:assert_custom_layer_groups_test_app_test [k8-fastbuild] 🔗
//js/private/test/image:assert_custom_owner_test_app_test [k8-fastbuild]                  🔗
//js/private/test/image:assert_default_test_app_test [k8-fastbuild]                       🔗
//js/private/test/image:assert_regex_edge_cases_test_app_test [k8-fastbuild]              🔗
//js/private/test:write_launcher_test [k8-fastbuild]                                      🔗

💡 To reproduce the build failures, run

bazel build //npm/private/test:__rollup-target

💡 To reproduce the test failures, run

bazel test //js/private/test/image:assert_regex_edge_cases_test_app_test //js/private/test/image:assert_default_test_app_test //js/private/test/image/non_ascii:assert_custom_layer_groups_test_app_test //js/private/test/image:assert_custom_owner_test_app_test //js/private/test:write_launcher_test

Bazel 8 (Test)

⚠️ Buildkite build #12170 failed.

//npm/private/test:__rollup-target failed to build

__rollup-target__js_binary failed: error executing Rollup command (from target //npm/private/test:__rollup-target) bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary empty.js --format cjs --file foo.js
 
Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
 
bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary: line 616:    11 Aborted                 (core dumped) "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} 0<&0 >> "$STDOUT_CAPTURE" 2>> "$STDERR_CAPTURE"
 
empty.jsfoo.js...
 
<--- Last few GCs --->
 
[11:0x9414000]   269824 ms: Scavenge (interleaved) 2045.2 (2083.5) -> 2043.8 (2083.5) MB, pooled: 0 MB, 7.86 / 0.00 ms  (average mu = 0.222, current mu = 0.175) task;
[11:0x9414000]   271172 ms: Mark-Compact (reduce) 2043.8 (2083.5) -> 2043.8 (2080.7) MB, pooled: 0 MB, 1293.43 / 0.00 ms  (+ 1.3 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 1300 ms) (average mu = 0.216, curre
 
<--- JS stacktrace --->
 
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
 
 1: 0xe40d24 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/1549/execroot/_main/bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary.runfiles/_main/../rules_nodejs++node+nodejs_linux_amd64/bin/nodejs/bin/node]
 2: 0x1216be0 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/1549/execroot/_main/bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary.runfiles/_main/../rules_nodejs++node+nodejs_linux_amd64/bin/nodejs/bin/node]
 3: 0x1216eb7 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/1549/execroot/_main/bazel-out/k8-opt-exec-ST-d57f47055a04/bin/npm/private/tes

... 1579 chars truncated

💡 To reproduce the build failures, run

bazel build //npm/private/test:__rollup-target

Bazel 9 (Test)

⚠️ Buildkite build #12170 failed.

//npm/private/test:__rollup-target failed to build

__rollup-target__js_binary failed: error executing Rollup command (from _run_binary rule target //npm/private/test:__rollup-target) bazel-out/k8-opt-exec/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary empty.js --format cjs --file foo.js
 
Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
 
bazel-out/k8-opt-exec/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary: line 616:    11 Aborted                 (core dumped) "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} 0<&0 >> "$STDOUT_CAPTURE" 2>> "$STDERR_CAPTURE"
 
empty.jsfoo.js...
 
<--- Last few GCs --->
 
[11:0x3a56e000]   291908 ms: Scavenge (interleaved) 2044.9 (2083.2) -> 2043.7 (2083.2) MB, pooled: 0 MB, 6.02 / 0.00 ms  (average mu = 0.260, current mu = 0.247) task;
[11:0x3a56e000]   294084 ms: Mark-Compact (reduce) 2043.7 (2083.2) -> 2043.7 (2080.5) MB, pooled: 0 MB, 2024.78 / 0.00 ms  (+ 1.3 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 2033 ms) (average mu = 0.196, curr
 
<--- JS stacktrace --->
 
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
 
 1: 0xe40d24 node::OOMErrorHandler(char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/1585/execroot/_main/bazel-out/k8-opt-exec/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary.runfiles/_main/../rules_nodejs++node+nodejs_linux_amd64/bin/nodejs/bin/node]
 2: 0x1216be0 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/1585/execroot/_main/bazel-out/k8-opt-exec/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary.runfiles/_main/../rules_nodejs++node+nodejs_linux_amd64/bin/nodejs/bin/node]
 3: 0x1216eb7 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [/mnt/ephemeral/output/rules_js/__main__/sandbox/linux-sandbox/1585/execroot/_main/bazel-out/k8-opt-exec/bin/npm/private/test/__rollup-target__js_binary_/__rollup-target__js_binary.runf

... 1438 chars truncated

💡 To reproduce the build failures, run

bazel build //npm/private/test:__rollup-target

Bazel 7 (Test)

e2e/bzlmod

2 test targets passed

Targets
//:jasmine_test [k8-fastbuild]                                                          124ms
//:test [k8-fastbuild]                                                                  126ms

Total test execution time was 250ms. 5 tests (71.4%) were fully cached saving 243ms.


Bazel 8 (Test)

e2e/bzlmod

2 test targets passed

Targets
//:jasmine_test [k8-fastbuild]                                                          166ms
//:test [k8-fastbuild]                                                                  128ms

Total test execution time was 294ms. 5 tests (71.4%) were fully cached saving 272ms.


Bazel 9 (Test)

e2e/bzlmod

2 test targets passed

Targets
//:jasmine_test [k8-fastbuild]                                                          140ms
//:test [k8-fastbuild]                                                                  331ms

Total test execution time was 471ms. 5 tests (71.4%) were fully cached saving 296ms.


Bazel 7 (Test)

e2e/git_dep_metadata

All tests were cache hits

1 test (100.0%) was fully cached saving 30ms.


Bazel 8 (Test)

e2e/git_dep_metadata

All tests were cache hits

1 test (100.0%) was fully cached saving 23ms.


Bazel 9 (Test)

e2e/git_dep_metadata

All tests were cache hits

1 test (100.0%) was fully cached saving 30ms.


Bazel 7 (Test)

e2e/gyp_no_install_script

1 test target passed

Targets
//:test [k8-fastbuild]                                                                  49ms

Total test execution time was 49ms. 1 test (50.0%) was fully cached saving 64ms.


Bazel 8 (Test)

e2e/gyp_no_install_script

1 test target passed

Targets
//:test [k8-fastbuild]                                                                  53ms

Bazel 9 (Test)

e2e/gyp_no_install_script

1 test target passed

Targets
//:test [k8-fastbuild]                                                                  50ms

Bazel 7 (Test)

e2e/js_binary_workspace

All tests were cache hits

1 test (100.0%) was fully cached saving 44ms.


Bazel 8 (Test)

e2e/js_binary_workspace

All tests were cache hits

1 test (100.0%) was fully cached saving 37ms.


Bazel 9 (Test)

e2e/js_binary_workspace

All tests were cache hits

1 test (100.0%) was fully cached saving 35ms.


Bazel 7 (Test)

e2e/js_image_oci

1 test target passed

Targets
//src:image_test [k8-fastbuild]                                                         4s

Bazel 7 (Test)

e2e/npm_link_package

1 test target passed

Targets
//src:test [k8-fastbuild]                                                               113ms

Total test execution time was 113ms. 1 test (50.0%) was fully cached saving 62ms.


Bazel 8 (Test)

e2e/npm_link_package

1 test target passed

Targets
//src:test [k8-fastbuild]                                                               124ms

Total test execution time was 124ms. 1 test (50.0%) was fully cached saving 53ms.


Bazel 9 (Test)

e2e/npm_link_package

1 test target passed

Targets
//src:test [k8-fastbuild]                                                               142ms

Total test execution time was 142ms. 1 test (50.0%) was fully cached saving 61ms.


Bazel 7 (Test)

e2e/npm_link_package-esm

1 test target passed

Targets
//src:test [k8-fastbuild]                                                               140ms

Total test execution time was 140ms. 1 test (50.0%) was fully cached saving 62ms.


Bazel 8 (Test)

e2e/npm_link_package-esm

1 test target passed

Targets
//src:test [k8-fastbuild]                                                               124ms

Total test execution time was 124ms. 1 test (50.0%) was fully cached saving 53ms.


Bazel 9 (Test)

e2e/npm_link_package-esm

1 test target passed

Targets
//src:test [k8-fastbuild]                                                               116ms

Total test execution time was 116ms. 1 test (50.0%) was fully cached saving 61ms.


Bazel 7 (Test)

e2e/npm_link_package-rerooted

1 test target passed

Targets
//root/src:test [k8-fastbuild]                                                          118ms

Total test execution time was 118ms. 1 test (50.0%) was fully cached saving 95ms.


Bazel 8 (Test)

e2e/npm_link_package-rerooted

1 test target passed

Targets
//root/src:test [k8-fastbuild]                                                          114ms

Total test execution time was 114ms. 1 test (50.0%) was fully cached saving 67ms.


Bazel 9 (Test)

e2e/npm_link_package-rerooted

1 test target passed

Targets
//root/src:test [k8-fastbuild]                                                          110ms

Total test execution time was 110ms. 1 test (50.0%) was fully cached saving 71ms.


Bazel 7 (Test)

e2e/npm_translate_lock

2 test targets passed

Targets
//:test_dev_filtering                                                                   131ms
//:test_prod_filtering                                                                  262ms

Total test execution time was 393ms. 1 test (33.3%) was fully cached saving 56ms.


Bazel 8 (Test)

e2e/npm_translate_lock

2 test targets passed

Targets
//:test_dev_filtering                                                                   80ms
//:test_prod_filtering                                                                  211ms

Total test execution time was 291ms. 1 test (33.3%) was fully cached saving 28ms.


Bazel 9 (Test)

e2e/npm_translate_lock

2 test targets passed

Targets
//:test_dev_filtering                                                                   83ms
//:test_prod_filtering                                                                  183ms

Total test execution time was 266ms. 1 test (33.3%) was fully cached saving 29ms.


Bazel 7 (Test)

e2e/npm_translate_lock_disable_hooks

All tests were cache hits

3 tests (100.0%) were fully cached saving 268ms.


Bazel 8 (Test)

e2e/npm_translate_lock_disable_hooks

All tests were cache hits

1 test (100.0%) was fully cached saving 26ms.


Bazel 9 (Test)

e2e/npm_translate_lock_disable_hooks

All tests were cache hits

1 test (100.0%) was fully cached saving 32ms.


Bazel 7 (Test)

e2e/npm_translate_lock_empty

All tests were cache hits

2 tests (100.0%) were fully cached saving 132ms.


Bazel 8 (Test)

e2e/npm_translate_lock_empty

All tests were cache hits

2 tests (100.0%) were fully cached saving 123ms.


Bazel 9 (Test)

e2e/npm_translate_lock_empty

All tests were cache hits

2 tests (100.0%) were fully cached saving 105ms.


Bazel 7 (Test)

e2e/npm_translate_lock_exclude_package_contents

All tests were cache hits

1 test (100.0%) was fully cached saving 33ms.


Bazel 8 (Test)

e2e/npm_translate_lock_exclude_package_contents

All tests were cache hits

1 test (100.0%) was fully cached saving 40ms.


Bazel 9 (Test)

e2e/npm_translate_lock_exclude_package_contents

All tests were cache hits

1 test (100.0%) was fully cached saving 86ms.


Bazel 7 (Test)

e2e/npm_translate_lock_multi

All tests were cache hits

2 tests (100.0%) were fully cached saving 106ms.


Bazel 8 (Test)

e2e/npm_translate_lock_multi

All tests were cache hits

2 tests (100.0%) were fully cached saving 125ms.


Bazel 9 (Test)

e2e/npm_translate_lock_multi

All tests were cache hits

2 tests (100.0%) were fully cached saving 113ms.


Bazel 7 (Test)

e2e/npm_translate_lock_partial_clone

All tests were cache hits

1 test (100.0%) was fully cached saving 26ms.


Bazel 8 (Test)

e2e/npm_translate_lock_partial_clone

All tests were cache hits

1 test (100.0%) was fully cached saving 28ms.


Bazel 9 (Test)

e2e/npm_translate_lock_partial_clone

All tests were cache hits

1 test (100.0%) was fully cached saving 38ms.


Bazel 7 (Test)

e2e/npm_translate_lock_replace_packages

2 test targets passed

Targets
//:test [k8-fastbuild]                                                                  67ms
//:utils_test [k8-fastbuild]                                                            57ms

Total test execution time was 124ms. 2 tests (50.0%) were fully cached saving 189ms.


Bazel 8 (Test)

e2e/npm_translate_lock_replace_packages

2 test targets passed

Targets
//:test [k8-fastbuild]                                                                  79ms
//:utils_test [k8-fastbuild]                                                            65ms

Total test execution time was 144ms. 2 tests (50.0%) were fully cached saving 140ms.


Bazel 9 (Test)

e2e/npm_translate_lock_replace_packages

2 test targets passed

Targets
//:test [k8-fastbuild]                                                                  71ms
//:utils_test [k8-fastbuild]                                                            56ms

Total test execution time was 127ms. 2 tests (50.0%) were fully cached saving 162ms.


Bazel 7 (Test)

e2e/npm_translate_lock_subdir_patch

1 test target passed

Targets
//subdir:test                                                                           63ms

Bazel 8 (Test)

e2e/npm_translate_lock_subdir_patch

1 test target passed

Targets
//subdir:test                                                                           64ms

Bazel 9 (Test)

e2e/npm_translate_lock_subdir_patch

1 test target passed

Targets
//subdir:test                                                                           56ms

Bazel 7 (Test)

e2e/npm_translate_package_lock

All tests were cache hits

1 test (100.0%) was fully cached saving 25ms.


Bazel 8 (Test)

e2e/npm_translate_package_lock

All tests were cache hits

1 test (100.0%) was fully cached saving 26ms.


Bazel 9 (Test)

e2e/npm_translate_package_lock

All tests were cache hits

1 test (100.0%) was fully cached saving 32ms.


Bazel 7 (Test)

e2e/npm_translate_yarn_lock

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/npm_translate_yarn_lock

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/npm_translate_yarn_lock

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/package_json_module

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/package_json_module

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/package_json_module

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/patch_from_repo

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/pnpm_lockfiles

5 test targets passed

Targets
//cases/versionless-patch-v9:versionless_patch_test [k8-fastbuild]                      165ms
//v101:aliases-test [k8-fastbuild]                                                      70ms
//v101:patch-test [k8-fastbuild]                                                        96ms
//v90:aliases-test [k8-fastbuild]                                                       146ms
//v90:patch-test [k8-fastbuild]                                                         156ms

Total test execution time was 633ms. 32 tests (86.5%) were fully cached saving 3s.


Bazel 8 (Test)

e2e/pnpm_lockfiles

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/pnpm_lockfiles

5 test targets passed

Targets
//cases/versionless-patch-v9:versionless_patch_test [k8-fastbuild]                      77ms
//v101:aliases-test [k8-fastbuild]                                                      107ms
//v101:patch-test [k8-fastbuild]                                                        172ms
//v90:aliases-test [k8-fastbuild]                                                       108ms
//v90:patch-test [k8-fastbuild]                                                         113ms

Total test execution time was 577ms. 6 tests (54.5%) were fully cached saving 281ms.


Bazel 7 (Test)

e2e/pnpm_repo_install

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/pnpm_repo_install

1 test target passed

Targets
//:pnpm_install_test                                                                    774ms

Bazel 9 (Test)

e2e/pnpm_repo_install

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/pnpm_version

1 test target passed

Targets
//:test                                                                                 44ms

Bazel 8 (Test)

e2e/pnpm_version

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/pnpm_version

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/pnpm_workspace

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/pnpm_workspace

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/pnpm_workspace

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/pnpm_workspace_deps

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/pnpm_workspace_deps

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/pnpm_workspace_deps

3 test targets passed

Targets
//tests:direct [k8-fastbuild]                                                           88ms
//tests:dupes [k8-fastbuild]                                                            80ms
//tests:pkg [k8-fastbuild]                                                              71ms

Bazel 7 (Test)

e2e/pnpm_workspace_rerooted

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/pnpm_workspace_rerooted

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/pnpm_workspace_rerooted

6 test targets passed

Targets
//app/a:aspect_test_a_bin_test [k8-fastbuild]                                           268ms
//app/a:test [k8-fastbuild]                                                             192ms
//app/b:test [k8-fastbuild]                                                             77ms
//app/c:aspect_test_a_bin_test [k8-fastbuild]                                           190ms
//app/c:test [k8-fastbuild]                                                             287ms
//app/d:test [k8-fastbuild]                                                             181ms

Total test execution time was 1s. 8 tests (57.1%) were fully cached saving 1s.


Bazel 7 (Test)

e2e/repo_mapping

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/repo_mapping

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/repo_mapping

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/runfiles

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/runfiles

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/runfiles

Waiting for runner...


Bazel 7 (Test)

e2e/stamped_package_json

Waiting for runner...


Bazel 8 (Test)

e2e/stamped_package_json

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/stamped_package_json

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/vendored_node

Buildkite build #12170 is running...


Bazel 8 (Test)

e2e/vendored_node

Waiting for runner...


Bazel 9 (Test)

e2e/vendored_node

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/vendored_tarfile

Waiting for runner...


Bazel 8 (Test)

e2e/vendored_tarfile

Buildkite build #12170 is running...


Bazel 9 (Test)

e2e/vendored_tarfile

All tests were cache hits

1 test (100.0%) was fully cached saving 32ms.


Bazel 7 (Test)

e2e/verify_patches

Waiting for runner...


Bazel 8 (Test)

e2e/verify_patches

All tests were cache hits

2 tests (100.0%) were fully cached saving 88ms.


Bazel 9 (Test)

e2e/verify_patches

Buildkite build #12170 is running...


Bazel 7 (Test)

e2e/worker

All tests were cache hits

1 test (100.0%) was fully cached saving 75ms.


Bazel 8 (Test)

e2e/worker

All tests were cache hits

1 test (100.0%) was fully cached saving 49ms.


Bazel 9 (Test)

e2e/worker

All tests were cache hits

1 test (100.0%) was fully cached saving 84ms.


Buildifier      Format

Mivr and others added 3 commits February 7, 2026 15:49
- Add load() statements for cc_library/cc_binary/cc_test from @rules_cc
  (required for Bazel 9+ where native cc rules are removed)
- Add rules_cc as a dependency in MODULE.bazel
- Add "//conditions:default" to select() in fs_patch_native alias with
  a noop genrule placeholder (fixes Windows where neither linux nor
  macos condition matches)
- Add cfg = "exec" to _fs_patch_native attr (fixes cross-compilation
  builds like transition_js_image_layer_linux_arm64 that need a CC
  toolchain for the target platform)
- Remove DYLD_INSERT_LIBRARIES on macOS (arm64/arm64e architecture
  incompatibility on macOS 15+ causes dyld abort; macOS relies on
  JS-level patches via register.cjs)
- Run buildifier for canonical attribute ordering
- Update image layer golden files (fs_patch_linux.so now in runfiles)
- Update launcher golden file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of using cfg="exec" to work around missing CC toolchains during
cross-compilation (e.g. transition_js_image_layer_linux_arm64), restrict
the native FS patch to Linux x86_64 via config_setting. All other
platforms get a noop placeholder, so no CC toolchain is needed.

The rule detects the noop by checking the file extension (.so/.dylib)
and skips setting LD_PRELOAD and adding to launcher_files when the
platform is unsupported.

Native FS patching is not supported on ARM or macOS for now:
- macOS: DYLD_INSERT_LIBRARIES requires arm64e on macOS 15+
- ARM: cross-compilation requires CC toolchain for target platform

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The .so binary size varies across compiler versions (17008 locally vs
17016 on CI). Extend the existing sanitize_cmd sed regex to replace
17XXX with xxxxx, space-anchored to avoid matching substrings of larger
numbers (e.g. 217950 in acorn.js).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Mivr Mivr marked this pull request as ready for review February 7, 2026 17:07
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90abd22b53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +265 to +269
if (resolved_path) {
memcpy(resolved_path, normed, strlen(normed) + 1);
return resolved_path;
} else {
return strdup(normed);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Return failure when unresolved path components remain

In the orig_lstat(next) != 0 branch, this code returns a synthesized normalized path when there is remaining tail (rest) instead of propagating the lookup failure. That makes calls like realpath("/sandbox/missing/child") succeed with "/sandbox/missing/child" rather than returning NULL/ENOENT, which changes libc/Node realpath semantics and can cause callers (including resolution logic) to treat non-existent paths as resolved. This branch should preserve the error from the failed component lookup rather than returning a fabricated success value.

Useful? React with 👍 / 👎.

Mivr and others added 2 commits February 8, 2026 17:10
Previously, when lstat failed on a path component with remaining
components, guarded_realpath returned a synthesized normalized path
instead of propagating ENOENT. This diverged from libc realpath()
semantics. Now always returns NULL with ENOENT when any component
doesn't exist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable DYLD_INSERT_LIBRARIES-based interposition on macOS to prevent
ESM imports, esbuild, Vite, and Vitest from escaping the Bazel sandbox.

Key changes:
- Add lstat interposition to fs_patch_macos.c (Tier 2) using fstatat
  to avoid DYLD interpose recursion where dlsym(RTLD_NEXT) returns
  the interposed function
- Add macOS arm64/x86_64 config settings to BUILD.bazel so the .dylib
  is selected instead of the noop placeholder
- Work around macOS SIP stripping DYLD_* env vars through /bin/bash by
  passing the dylib path via JS_BINARY__NATIVE_PATCH_PATH and having
  node_wrapper.sh restore DYLD_INSERT_LIBRARIES before exec'ing node

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant