diff --git a/.gitattributes b/.gitattributes index e693f6782..7c0a76dc8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,4 +6,5 @@ js/private/coverage/coverage.js linguist-generated=true js/private/devserver/js_run_devserver.mjs linguist-generated=true js/private/watch/aspect_watch_protocol.mjs linguist-generated=true js/private/watch/aspect_watch_protocol.d.mts linguist-generated=true +js/private/fs.*.cjs linguist-generated=true js/private/js_image_layer.mjs linguist-generated=true diff --git a/.prettierignore b/.prettierignore index 59e391e51..84c376e1e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,6 +6,7 @@ examples/**/*-docs.md js/private/coverage/coverage.js js/private/devserver/js_run_devserver.mjs js/private/node-patches/fs.cjs +js/private/node-patches/fs_stat.cjs js/private/watch/aspect_watch_protocol.mjs js/private/watch/aspect_watch_protocol.d.mts min/ diff --git a/MODULE.bazel b/MODULE.bazel index 538c8c71d..4bde9792a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -13,7 +13,7 @@ bazel_dep(name = "aspect_tools_telemetry", version = "0.2.8") bazel_dep(name = "bazel_features", version = "1.9.0") bazel_dep(name = "bazel_skylib", version = "1.5.0") bazel_dep(name = "platforms", version = "0.0.5") -bazel_dep(name = "rules_nodejs", version = "6.3.0") +bazel_dep(name = "rules_nodejs", version = "6.4.0") tel = use_extension("@aspect_tools_telemetry//:extension.bzl", "telemetry") use_repo(tel, "aspect_tools_telemetry_report") diff --git a/js/private/js_binary.bzl b/js/private/js_binary.bzl index d49d1e235..18d1924ec 100644 --- a/js/private/js_binary.bzl +++ b/js/private/js_binary.bzl @@ -204,6 +204,13 @@ _ATTRS = { which can lead to non-hermetic behavior.""", default = True, ), + "patch_node_esm_loader": attr.bool( + doc = """Apply the internal lstat patch to prevent the program from following symlinks out of + the execroot, runfiles and the sandbox even when using the ESM loader. + + This flag only has an effect when `patch_node_fs` is True.""", + default = True, + ), "include_sources": attr.bool( doc = """When True, `sources` from `JsInfo` providers in `data` targets are included in the runfiles of the target.""", default = True, @@ -319,7 +326,10 @@ _ATTRS = { "_windows_constraint": attr.label(default = "@platforms//os:windows"), "_node_patches_files": attr.label_list( allow_files = True, - default = [Label("@aspect_rules_js//js/private/node-patches:fs.cjs")], + default = [ + Label("@aspect_rules_js//js/private/node-patches:fs.cjs"), + Label("@aspect_rules_js//js/private/node-patches:fs_stat.cjs"), + ], ), "_node_patches": attr.label( allow_single_file = True, @@ -564,11 +574,18 @@ def _create_launcher(ctx, log_prefix_rule_set, log_prefix_rule, fixed_args = [], ) def _js_binary_impl(ctx): + # Only apply lstat patch if it's requested + JS_BINARY__PATCH_NODE_ESM_LOADER = "1" if ctx.attr.patch_node_esm_loader else "0" + fixed_env = { + "JS_BINARY__PATCH_NODE_ESM_LOADER": JS_BINARY__PATCH_NODE_ESM_LOADER, + } + launcher = _create_launcher( ctx, log_prefix_rule_set = "aspect_rules_js", log_prefix_rule = "js_test" if ctx.attr.testonly else "js_binary", fixed_args = ctx.attr.fixed_args, + fixed_env = fixed_env, ) runfiles = launcher.runfiles diff --git a/js/private/node-patches/BUILD.bazel b/js/private/node-patches/BUILD.bazel index d6af777ee..ae23a883f 100644 --- a/js/private/node-patches/BUILD.bazel +++ b/js/private/node-patches/BUILD.bazel @@ -4,10 +4,12 @@ write_source_files( name = "checked_in_compile", files = { "fs.cjs": "//js/private/node-patches/src:fs-generated.cjs", + "fs_stat.cjs": "//js/private/node-patches/src:fs_stat.cjs", }, ) exports_files([ "fs.cjs", + "fs_stat.cjs", "register.cjs", ]) diff --git a/js/private/node-patches/fs.cjs b/js/private/node-patches/fs.cjs index 3b7a267bd..38137fe72 100644 --- a/js/private/node-patches/fs.cjs +++ b/js/private/node-patches/fs.cjs @@ -39,6 +39,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar Object.defineProperty(exports, "__esModule", { value: true }); exports.patcher = patcher; exports.isSubPath = isSubPath; +exports.resolvePathLike = resolvePathLike; exports.escapeFunction = escapeFunction; const path = require("path"); const util = require("util"); @@ -65,7 +66,7 @@ const PATCHED_FS_METHODS = [ * Function that patches the `fs` module to not escape the given roots. * @returns a function to undo the patches. */ -function patcher(roots) { +function patcher(roots, useInternalLstatPatch = false) { if (fs._unpatched) { throw new Error('FS is already patched.'); } @@ -100,82 +101,93 @@ function patcher(roots) { .native; const { canEscape, isEscape } = escapeFunction(roots); // ========================================================================= + // fsInternal.lstat (to patch ESM resolve's `realpathSync`!) + // ========================================================================= + let unpatchEsm; + if (useInternalLstatPatch) { + const lstatEsmPatcher = new (require('./fs_stat.cjs').FsInternalStatPatcher)({ canEscape, isEscape }, guardedReadLink, guardedReadLinkSync, unguardedRealPath, unguardedRealPathSync); + lstatEsmPatcher.patch(); + unpatchEsm = lstatEsmPatcher.revert.bind(lstatEsmPatcher); + } + // ========================================================================= // fs.lstat // ========================================================================= - fs.lstat = function lstat(...args) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origLstat(...args); - } - const cb = once(args[args.length - 1]); - // override the callback - args[args.length - 1] = function lstatCb(err, stats) { - if (err) - return cb(err); - if (!stats.isSymbolicLink()) { + if (!useInternalLstatPatch) { + fs.lstat = function lstat(...args) { + // preserve error when calling function without required callback + if (typeof args[args.length - 1] !== 'function') { + return origLstat(...args); + } + const cb = once(args[args.length - 1]); + // override the callback + args[args.length - 1] = function lstatCb(err, stats) { + if (err) + return cb(err); + if (!stats.isSymbolicLink()) { + // the file is not a symbolic link so there is nothing more to do + return cb(null, stats); + } + args[0] = resolvePathLike(args[0]); + if (!canEscape(args[0])) { + // the file can not escaped the sandbox so there is nothing more to do + return cb(null, stats); + } + return guardedReadLink(args[0], guardedReadLinkCb); + function guardedReadLinkCb(str) { + if (str != args[0]) { + // there are one or more hops within the guards so there is nothing more to do + return cb(null, stats); + } + // there are no hops so lets report the stats of the real file; + // we can't use origRealPath here since that function calls lstat internally + // which can result in an infinite loop + return unguardedRealPath(args[0], unguardedRealPathCb); + function unguardedRealPathCb(err, str) { + if (err) { + if (err.code === 'ENOENT') { + // broken link so there is nothing more to do + return cb(null, stats); + } + return cb(err); + } + return origLstat(str, cb); + } + } + }; + origLstat(...args); + }; + fs.lstatSync = function lstatSync(...args) { + const stats = origLstatSync(...args); + if (!(stats === null || stats === void 0 ? void 0 : stats.isSymbolicLink())) { // the file is not a symbolic link so there is nothing more to do - return cb(null, stats); + return stats; } args[0] = resolvePathLike(args[0]); if (!canEscape(args[0])) { // the file can not escaped the sandbox so there is nothing more to do - return cb(null, stats); + return stats; } - return guardedReadLink(args[0], guardedReadLinkCb); - function guardedReadLinkCb(str) { - if (str != args[0]) { - // there are one or more hops within the guards so there is nothing more to do - return cb(null, stats); - } + const guardedReadLink = guardedReadLinkSync(args[0]); + if (guardedReadLink != args[0]) { + // there are one or more hops within the guards so there is nothing more to do + return stats; + } + try { + args[0] = unguardedRealPathSync(args[0]); // there are no hops so lets report the stats of the real file; - // we can't use origRealPath here since that function calls lstat internally + // we can't use origRealPathSync here since that function calls lstat internally // which can result in an infinite loop - return unguardedRealPath(args[0], unguardedRealPathCb); - function unguardedRealPathCb(err, str) { - if (err) { - if (err.code === 'ENOENT') { - // broken link so there is nothing more to do - return cb(null, stats); - } - return cb(err); - } - return origLstat(str, cb); + return origLstatSync(...args); + } + catch (err) { + if (err.code === 'ENOENT') { + // broken link so there is nothing more to do + return stats; } + throw err; } }; - origLstat(...args); - }; - fs.lstatSync = function lstatSync(...args) { - const stats = origLstatSync(...args); - if (!(stats === null || stats === void 0 ? void 0 : stats.isSymbolicLink())) { - // the file is not a symbolic link so there is nothing more to do - return stats; - } - args[0] = resolvePathLike(args[0]); - if (!canEscape(args[0])) { - // the file can not escaped the sandbox so there is nothing more to do - return stats; - } - const guardedReadLink = guardedReadLinkSync(args[0]); - if (guardedReadLink != args[0]) { - // there are one or more hops within the guards so there is nothing more to do - return stats; - } - try { - args[0] = unguardedRealPathSync(args[0]); - // there are no hops so lets report the stats of the real file; - // we can't use origRealPathSync here since that function calls lstat internally - // which can result in an infinite loop - return origLstatSync(...args); - } - catch (err) { - if (err.code === 'ENOENT') { - // broken link so there is nothing more to do - return stats; - } - throw err; - } - }; + } // ========================================================================= // fs.realpath // ========================================================================= @@ -388,7 +400,9 @@ function patcher(roots) { let unpatchPromises; if (promisePropertyDescriptor) { const promises = {}; - promises.lstat = util.promisify(fs.lstat); + if (!useInternalLstatPatch) { + promises.lstat = util.promisify(fs.lstat); + } // NOTE: node core uses the newer realpath function fs.promises.native instead of fs.realPath promises.realpath = util.promisify(fs.realpath.native); promises.readlink = util.promisify(fs.readlink); @@ -765,6 +779,9 @@ function patcher(roots) { if (unpatchPromises) { unpatchPromises(); } + if (unpatchEsm) { + unpatchEsm(); + } // Re-sync the esm modules to revert to the unpatched module. esmModule.syncBuiltinESMExports(); }; diff --git a/js/private/node-patches/fs_stat.cjs b/js/private/node-patches/fs_stat.cjs new file mode 100644 index 000000000..c4ce5e11b --- /dev/null +++ b/js/private/node-patches/fs_stat.cjs @@ -0,0 +1,121 @@ +"use strict"; +// Patches Node's internal FS bindings, right before they would call into C++. +// See full context in: https://github.com/aspect-build/rules_js/issues/362. +// This is to ensure ESM imports don't escape accidentally via `realpathSync`. +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FsInternalStatPatcher = void 0; +/// +const binding_1 = require("internal/test/binding"); +const utils_1 = require("internal/fs/utils"); +const fs_cjs_1 = require("./fs.cjs"); +const internalFs = (0, binding_1.internalBinding)('fs'); +class FsInternalStatPatcher { + constructor(escapeFns, guardedReadLink, guardedReadLinkSync, unguardedRealPath, unguardedRealPathSync) { + this.escapeFns = escapeFns; + this.guardedReadLink = guardedReadLink; + this.guardedReadLinkSync = guardedReadLinkSync; + this.unguardedRealPath = unguardedRealPath; + this.unguardedRealPathSync = unguardedRealPathSync; + this._originalFsLstat = internalFs.lstat; + } + revert() { + internalFs.lstat = this._originalFsLstat; + } + patch() { + const statPatcher = this; + internalFs.lstat = function (path, bigint, reqCallback, throwIfNoEntry) { + if (reqCallback === internalFs.kUsePromises) { + return statPatcher._originalFsLstat.call(internalFs, path, bigint, reqCallback, throwIfNoEntry).then((stats) => { + return new Promise((resolve, reject) => { + statPatcher.eeguardStats(path, bigint, stats, throwIfNoEntry, (err, guardedStats) => { + err || !guardedStats ? reject(err) : resolve(guardedStats); + }); + }); + }); + } + else if (reqCallback === undefined) { + const stats = statPatcher._originalFsLstat.call(internalFs, path, bigint, undefined, throwIfNoEntry); + if (!stats) { + return stats; + } + return statPatcher.eeguardStatsSync(path, bigint, throwIfNoEntry, stats); + } + else { + // Just re-use the promise path from above. + internalFs.lstat(path, bigint, internalFs.kUsePromises, throwIfNoEntry) + .then((stats) => reqCallback.oncomplete(null, stats)) + .catch((err) => reqCallback.oncomplete(err)); + } + }; + } + eeguardStats(path, bigint, stats, throwIfNotFound, cb) { + const statsObj = (0, utils_1.getStatsFromBinding)(stats); + if (!statsObj.isSymbolicLink()) { + // the file is not a symbolic link so there is nothing more to do + return cb(null, stats); + } + path = (0, fs_cjs_1.resolvePathLike)(path); + if (!this.escapeFns.canEscape(path)) { + // the file can not escaped the sandbox so there is nothing more to do + return cb(null, stats); + } + return this.guardedReadLink(path, (str) => { + if (str != path) { + // there are one or more hops within the guards so there is nothing more to do + return cb(null, stats); + } + // there are no hops so lets report the stats of the real file; + // we can't use origRealPath here since that function calls lstat internally + // which can result in an infinite loop + return this.unguardedRealPath(path, (err, str) => { + if (err) { + if (err.code === 'ENOENT') { + // broken link so there is nothing more to do + return cb(null, stats); + } + return cb(err); + } + // Forward request to original callback. + const req2 = new internalFs.FSReqCallback(bigint); + req2.oncomplete = (err, realStats) => cb(err, realStats); + return this._originalFsLstat.call(internalFs, str, bigint, req2, throwIfNotFound); + }); + }); + } + eeguardStatsSync(path, bigint, throwIfNoEntry, stats) { + // No stats available. + if (!stats) { + return stats; + } + const statsObj = (0, utils_1.getStatsFromBinding)(stats); + if (!statsObj.isSymbolicLink()) { + // the file is not a symbolic link so there is nothing more to do + return stats; + } + path = (0, fs_cjs_1.resolvePathLike)(path); + if (!this.escapeFns.canEscape(path)) { + // the file can not escaped the sandbox so there is nothing more to do + return stats; + } + const guardedReadLink = this.guardedReadLinkSync(path); + if (guardedReadLink != path) { + // there are one or more hops within the guards so there is nothing more to do + return stats; + } + try { + path = this.unguardedRealPathSync(path); + // there are no hops so lets report the stats of the real file; + // we can't use origRealPathSync here since that function calls lstat internally + // which can result in an infinite loop + return this._originalFsLstat.call(internalFs, path, bigint, undefined, throwIfNoEntry); + } + catch (err) { + if (err.code === 'ENOENT') { + // broken link so there is nothing more to do + return stats; + } + throw err; + } + } +} +exports.FsInternalStatPatcher = FsInternalStatPatcher; diff --git a/js/private/node-patches/register.cjs b/js/private/node-patches/register.cjs index 85d67ddf9..5fd10591b 100644 --- a/js/private/node-patches/register.cjs +++ b/js/private/node-patches/register.cjs @@ -5,6 +5,7 @@ const { JS_BINARY__LOG_PREFIX, JS_BINARY__NODE_WRAPPER, JS_BINARY__PATCH_NODE_FS, + JS_BINARY__PATCH_NODE_ESM_LOADER, } = process.env // Keep a count of how many times these patches are applied; this should reflect the depth @@ -41,5 +42,8 @@ if ( `DEBUG: ${JS_BINARY__LOG_PREFIX}: node fs patches will be applied with roots: ${roots}` ) } - patchfs(roots) + const useLstatPatch = + JS_BINARY__PATCH_NODE_ESM_LOADER && + JS_BINARY__PATCH_NODE_ESM_LOADER != '0' + patchfs(roots, useLstatPatch) } diff --git a/js/private/node-patches/src/BUILD.bazel b/js/private/node-patches/src/BUILD.bazel index 5ed1769c5..ea19c014f 100644 --- a/js/private/node-patches/src/BUILD.bazel +++ b/js/private/node-patches/src/BUILD.bazel @@ -4,18 +4,24 @@ typescript_bin.tsc( name = "compile", srcs = [ "fs.cts", + "fs_stat.cts", + "fs_stat_types.d.cts", "tsconfig.json", "//:node_modules/@types/node", ], outs = [ "fs.cjs", + "fs_stat.cjs", ], args = [ "-p", "tsconfig.json", ], chdir = package_name(), - visibility = ["//js/private/test/node-patches:__pkg__"], + visibility = [ + "//js/private/node-patches:__pkg__", + "//js/private/test/node-patches:__pkg__", + ], ) genrule( diff --git a/js/private/node-patches/src/fs.cts b/js/private/node-patches/src/fs.cts index f5767ba66..a5d2592ca 100644 --- a/js/private/node-patches/src/fs.cts +++ b/js/private/node-patches/src/fs.cts @@ -53,7 +53,10 @@ const PATCHED_FS_METHODS: ReadonlyArray = [ * Function that patches the `fs` module to not escape the given roots. * @returns a function to undo the patches. */ -export function patcher(roots: string[]): () => void { +export function patcher( + roots: string[], + useInternalLstatPatch: boolean = false +): () => void { if (fs._unpatched) { throw new Error('FS is already patched.') } @@ -102,98 +105,119 @@ export function patcher(roots: string[]): () => void { const { canEscape, isEscape } = escapeFunction(roots) // ========================================================================= - // fs.lstat + // fsInternal.lstat (to patch ESM resolve's `realpathSync`!) // ========================================================================= + let unpatchEsm: Function | undefined + if (useInternalLstatPatch) { + const lstatEsmPatcher = + new (require('./fs_stat.cjs').FsInternalStatPatcher)( + { canEscape, isEscape }, + guardedReadLink, + guardedReadLinkSync, + unguardedRealPath, + unguardedRealPathSync + ) - fs.lstat = function lstat(...args: Parameters) { - // preserve error when calling function without required callback - if (typeof args[args.length - 1] !== 'function') { - return origLstat(...args) - } + lstatEsmPatcher.patch() - const cb = once(args[args.length - 1] as Function) + unpatchEsm = lstatEsmPatcher.revert.bind(lstatEsmPatcher) + } - // override the callback - args[args.length - 1] = function lstatCb(err: Error, stats: Stats) { - if (err) return cb(err) + // ========================================================================= + // fs.lstat + // ========================================================================= - if (!stats.isSymbolicLink()) { - // the file is not a symbolic link so there is nothing more to do - return cb(null, stats) + if (!useInternalLstatPatch) { + fs.lstat = function lstat(...args: Parameters) { + // preserve error when calling function without required callback + if (typeof args[args.length - 1] !== 'function') { + return origLstat(...args) } - args[0] = resolvePathLike(args[0]) + const cb = once(args[args.length - 1] as any) - if (!canEscape(args[0])) { - // the file can not escaped the sandbox so there is nothing more to do - return cb(null, stats) - } + // override the callback + args[args.length - 1] = function lstatCb(err: Error, stats: Stats) { + if (err) return cb(err) - return guardedReadLink(args[0], guardedReadLinkCb) + if (!stats.isSymbolicLink()) { + // the file is not a symbolic link so there is nothing more to do + return cb(null, stats) + } - function guardedReadLinkCb(str: string) { - if (str != args[0]) { - // there are one or more hops within the guards so there is nothing more to do + args[0] = resolvePathLike(args[0]) + + if (!canEscape(args[0])) { + // the file can not escaped the sandbox so there is nothing more to do return cb(null, stats) } - // there are no hops so lets report the stats of the real file; - // we can't use origRealPath here since that function calls lstat internally - // which can result in an infinite loop - return unguardedRealPath(args[0], unguardedRealPathCb) + return guardedReadLink(args[0], guardedReadLinkCb) - function unguardedRealPathCb(err: Error, str: string) { - if (err) { - if ((err as any).code === 'ENOENT') { - // broken link so there is nothing more to do - return cb(null, stats) + function guardedReadLinkCb(str: string) { + if (str != args[0]) { + // there are one or more hops within the guards so there is nothing more to do + return cb(null, stats) + } + + // there are no hops so lets report the stats of the real file; + // we can't use origRealPath here since that function calls lstat internally + // which can result in an infinite loop + return unguardedRealPath(args[0], unguardedRealPathCb) + + function unguardedRealPathCb(err: Error, str: string) { + if (err) { + if ((err as any).code === 'ENOENT') { + // broken link so there is nothing more to do + return cb(null, stats) + } + return cb(err) } - return cb(err) + return origLstat(str, cb) } - return origLstat(str, cb) } } - } - origLstat(...args) - } - - fs.lstatSync = function lstatSync( - ...args: Parameters - ) { - const stats = origLstatSync(...args) - - if (!stats?.isSymbolicLink()) { - // the file is not a symbolic link so there is nothing more to do - return stats + origLstat(...args) } - args[0] = resolvePathLike(args[0]) + fs.lstatSync = function lstatSync( + ...args: Parameters + ) { + const stats = origLstatSync(...args) - if (!canEscape(args[0])) { - // the file can not escaped the sandbox so there is nothing more to do - return stats - } + if (!stats?.isSymbolicLink()) { + // the file is not a symbolic link so there is nothing more to do + return stats + } - const guardedReadLink: string = guardedReadLinkSync(args[0]) - if (guardedReadLink != args[0]) { - // there are one or more hops within the guards so there is nothing more to do - return stats - } + args[0] = resolvePathLike(args[0]) - try { - args[0] = unguardedRealPathSync(args[0]) + if (!canEscape(args[0])) { + // the file can not escaped the sandbox so there is nothing more to do + return stats + } - // there are no hops so lets report the stats of the real file; - // we can't use origRealPathSync here since that function calls lstat internally - // which can result in an infinite loop - return origLstatSync(...args) - } catch (err) { - if (err.code === 'ENOENT') { - // broken link so there is nothing more to do + const guardedReadLink: string = guardedReadLinkSync(args[0]) + if (guardedReadLink != args[0]) { + // there are one or more hops within the guards so there is nothing more to do return stats } - throw err + + try { + args[0] = unguardedRealPathSync(args[0]) + + // there are no hops so lets report the stats of the real file; + // we can't use origRealPathSync here since that function calls lstat internally + // which can result in an infinite loop + return origLstatSync(...args) + } catch (err) { + if (err.code === 'ENOENT') { + // broken link so there is nothing more to do + return stats + } + throw err + } } } @@ -461,7 +485,9 @@ export function patcher(roots: string[]): () => void { if (promisePropertyDescriptor) { const promises: typeof fs.promises = {} - promises.lstat = util.promisify(fs.lstat) + if (!useInternalLstatPatch) { + promises.lstat = util.promisify(fs.lstat) + } // NOTE: node core uses the newer realpath function fs.promises.native instead of fs.realPath promises.realpath = util.promisify(fs.realpath.native) promises.readlink = util.promisify(fs.readlink) @@ -886,6 +912,10 @@ export function patcher(roots: string[]): () => void { unpatchPromises() } + if (unpatchEsm) { + unpatchEsm() + } + // Re-sync the esm modules to revert to the unpatched module. esmModule.syncBuiltinESMExports() } @@ -910,7 +940,7 @@ function stringifyPathLike(p: PathLike): string { } } -function resolvePathLike(p: PathLike): string { +export function resolvePathLike(p: PathLike): string { return path.resolve(stringifyPathLike(p)) } diff --git a/js/private/node-patches/src/fs_stat.cts b/js/private/node-patches/src/fs_stat.cts new file mode 100644 index 000000000..f59fe97b5 --- /dev/null +++ b/js/private/node-patches/src/fs_stat.cts @@ -0,0 +1,185 @@ +// Patches Node's internal FS bindings, right before they would call into C++. +// See full context in: https://github.com/aspect-build/rules_js/issues/362. +// This is to ensure ESM imports don't escape accidentally via `realpathSync`. + +/// + +import { internalBinding, FsInternalModule } from 'internal/test/binding'; +import { getStatsFromBinding } from 'internal/fs/utils'; +import { resolvePathLike, type escapeFunction } from './fs.cjs'; + +const internalFs = internalBinding('fs'); + +export class FsInternalStatPatcher { + private _originalFsLstat = internalFs.lstat; + + constructor( + private escapeFns: ReturnType, + private guardedReadLink: (start: string, cb: (str: string) => void) => void, + private guardedReadLinkSync: (start: string) => string, + private unguardedRealPath: (start: string, cb: (err: Error, str?: string) => void) => void, + private unguardedRealPathSync: (start: string) => string, + ) {} + + revert() { + internalFs.lstat = this._originalFsLstat; + } + + patch() { + const statPatcher = this; + + internalFs.lstat = function (path, bigint, reqCallback, throwIfNoEntry) { + const currentStack = new Error().stack; + const needsGuarding = + currentStack && + (currentStack.includes('finalizeResolution (node:internal/modules/esm/resolve') && + !currentStack.includes('eeguardStats')); + + if (!needsGuarding) { + console.log("NO NEED: " + currentStack); + return statPatcher._originalFsLstat.call( + internalFs, + path, + bigint, + reqCallback, + throwIfNoEntry, + ); + } + + if (reqCallback === internalFs.kUsePromises) { + return ( + statPatcher._originalFsLstat.call( + internalFs, + path, + bigint, + reqCallback, + throwIfNoEntry, + ) as Promise + ).then((stats) => { + return new Promise((resolve, reject) => { + statPatcher.eeguardStats(path, bigint, stats, throwIfNoEntry, (err, guardedStats) => { + err || !guardedStats ? reject(err) : resolve(guardedStats); + }); + }); + }); + } else if (reqCallback === undefined) { + const stats = statPatcher._originalFsLstat.call( + internalFs, + path, + bigint, + undefined, + throwIfNoEntry, + ) as FsInternalModule.InternalStats; + if (!stats) { + return stats; + } + return statPatcher.eeguardStatsSync(path, bigint, throwIfNoEntry, stats); + } else { + // Just re-use the promise path from above. + ( + internalFs.lstat( + path, + bigint, + internalFs.kUsePromises, + throwIfNoEntry, + ) as Promise + ) + .then((stats) => reqCallback.oncomplete(null, stats)) + .catch((err) => reqCallback.oncomplete(err)); + } + }; + } + + eeguardStats( + path: string, + bigint: boolean, + stats: FsInternalModule.InternalStats, + throwIfNotFound: boolean, + cb: (err: unknown, stats?: FsInternalModule.InternalStats) => void, + ) { + const statsObj = getStatsFromBinding(stats); + if (!statsObj.isSymbolicLink()) { + // the file is not a symbolic link so there is nothing more to do + return cb(null, stats); + } + + path = resolvePathLike(path); + if (!this.escapeFns.canEscape(path)) { + // the file can not escaped the sandbox so there is nothing more to do + return cb(null, stats); + } + + return this.guardedReadLink(path, (str) => { + if (str != path) { + // there are one or more hops within the guards so there is nothing more to do + return cb(null, stats); + } + // there are no hops so lets report the stats of the real file; + // we can't use origRealPath here since that function calls lstat internally + // which can result in an infinite loop + return this.unguardedRealPath(path, (err, str) => { + if (err) { + if ((err as Partial<{ code: string }>).code === 'ENOENT') { + // broken link so there is nothing more to do + return cb(null, stats); + } + return cb(err); + } + + // Forward request to original callback. + const req2 = new internalFs.FSReqCallback(bigint); + req2.oncomplete = (err, realStats) => cb(err, realStats); + return this._originalFsLstat.call(internalFs, str!, bigint, req2, throwIfNotFound); + }); + }); + } + + eeguardStatsSync( + path: string, + bigint: boolean, + throwIfNoEntry: boolean, + stats: FsInternalModule.InternalStats, + ): FsInternalModule.InternalStats { + // No stats available. + if (!stats) { + return stats; + } + + const statsObj = getStatsFromBinding(stats); + if (!statsObj.isSymbolicLink()) { + // the file is not a symbolic link so there is nothing more to do + return stats; + } + + path = resolvePathLike(path); + if (!this.escapeFns.canEscape(path)) { + // the file can not escaped the sandbox so there is nothing more to do + return stats; + } + + const guardedReadLink = this.guardedReadLinkSync(path); + if (guardedReadLink != path) { + // there are one or more hops within the guards so there is nothing more to do + return stats; + } + try { + path = this.unguardedRealPathSync(path); + // there are no hops so lets report the stats of the real file; + // we can't use origRealPathSync here since that function calls lstat internally + // which can result in an infinite loop + return this._originalFsLstat.call( + internalFs, + path, + bigint, + undefined, + throwIfNoEntry, + ) as FsInternalModule.InternalStats; + } catch (err) { + if ((err as Partial<{ code: string }>).code === 'ENOENT') { + // broken link so there is nothing more to do + return stats; + } + throw err; + } + } +} diff --git a/js/private/node-patches/src/fs_stat_types.d.cts b/js/private/node-patches/src/fs_stat_types.d.cts new file mode 100644 index 000000000..ff5266add --- /dev/null +++ b/js/private/node-patches/src/fs_stat_types.d.cts @@ -0,0 +1,31 @@ +// Types of internal modules exposes via `--expose-internals`. +// See: https://github.com/nodejs/node/blob/f58613a64c8e02b42391952a6e55a330a7607fa7/typings/internalBinding/fs.d.ts#L17. + +declare module 'internal/test/binding' { + namespace FsInternalModule { + type InternalStats = { readonly __internalStatsBrandedType: unique symbol }; + + const kUsePromises: unique symbol; + + class FSReqCallback { + constructor(bigint: boolean); + oncomplete: (err: unknown, stats?: InternalStats) => void; + } + + function lstat( + path: string, + bigint: boolean, + cb: typeof kUsePromises | FSReqCallback | undefined, + throwIfNotFound: boolean, + ): InternalStats | Promise | undefined; + } + + function internalBinding(module: 'fs'): typeof FsInternalModule; +} + +declare module 'internal/fs/utils' { + import type { Stats } from 'node:fs'; + import type { FsInternalModule } from 'internal/test/binding'; + + function getStatsFromBinding(stat: FsInternalModule.InternalStats): Stats; +} diff --git a/js/private/node-patches/src/tsconfig.json b/js/private/node-patches/src/tsconfig.json index c8f887d64..1f494a3ef 100644 --- a/js/private/node-patches/src/tsconfig.json +++ b/js/private/node-patches/src/tsconfig.json @@ -1,7 +1,9 @@ { "compilerOptions": { "module": "commonjs", - "target": "es2017" + "target": "es2017", + "moduleResolution": "node", + "types": ["node"] }, "include": ["*.cts"] } diff --git a/js/private/node_wrapper.sh b/js/private/node_wrapper.sh index fae2c2a56..511a900c5 100755 --- a/js/private/node_wrapper.sh +++ b/js/private/node_wrapper.sh @@ -2,4 +2,15 @@ set -o pipefail -o errexit -o nounset -exec "$JS_BINARY__NODE_BINARY" --require "$JS_BINARY__NODE_PATCHES" "$@" +if [[ "${JS_BINARY__PATCH_NODE_ESM_LOADER:-}" == "1" ]]; then + # --expose-internals is needed for FS esm patches. + + #--disable-warning=internal/test/binding + + exec "$JS_BINARY__NODE_BINARY" \ + --expose-internals \ + --inspect-brk \ + --require "$JS_BINARY__NODE_PATCHES" "$@" +else + exec "$JS_BINARY__NODE_BINARY" --require "$JS_BINARY__NODE_PATCHES" "$@" +fi diff --git a/js/private/test/image/checksum_test.expected b/js/private/test/image/checksum_test.expected index eedb9795b..a77b42c51 100644 --- a/js/private/test/image/checksum_test.expected +++ b/js/private/test/image/checksum_test.expected @@ -1,4 +1,4 @@ -979f06636582a6d515442b13bb048db0b66ed2febfa458a02d8745b691e9b2ff js/private/test/image/cksum_node +48120e2802ab300d1125df6c6f52351aec81fab8427a92c39eebea2d402257b6 js/private/test/image/cksum_node 52836a988c8ac815b4a3b70fa3a3acec67b851699fa989694cef4cc1fa53de96 js/private/test/image/cksum_package_store_3p 642b308a0561fb51dfd96d08d74a4ec419c9d2ca501cfa1002a49c8e25fbe4c2 js/private/test/image/cksum_package_store_1p 5d45f32dacf0b83e26c33d4e1017c694e79eaff29b8ecc336f9ea8fdee870d45 js/private/test/image/cksum_node_modules diff --git a/js/private/test/image/custom_layers_nomatch_test_node.listing b/js/private/test/image/custom_layers_nomatch_test_node.listing index c789e0f0c..97fed4a65 100644 --- a/js/private/test/image/custom_layers_nomatch_test_node.listing +++ b/js/private/test/image/custom_layers_nomatch_test_node.listing @@ -8,8 +8,9 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin. drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/ --r-xr-xr-x 0 0 0 34656 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs --r-xr-xr-x 0 0 0 1460 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs +-r-xr-xr-x 0 0 0 35421 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 0 0 6009 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs_stat.cjs +-r-xr-xr-x 0 0 0 1631 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/ diff --git a/js/private/test/image/custom_owner_test_app.listing b/js/private/test/image/custom_owner_test_app.listing index 40e19a484..bb3843353 100644 --- a/js/private/test/image/custom_owner_test_app.listing +++ b/js/private/test/image/custom_owner_test_app.listing @@ -2,7 +2,7 @@ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/ --r-xr-xr-x 0 100 0 23981 Jan 1 1970 ./js/private/test/image/bin +-r-xr-xr-x 0 100 0 24025 Jan 1 1970 ./js/private/test/image/bin drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/examples/ @@ -16,7 +16,7 @@ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runf drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/ --r-xr-xr-x 0 100 0 23981 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin +-r-xr-xr-x 0 100 0 24025 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/ --r-xr-xr-x 0 100 0 133 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/node +-r-xr-xr-x 0 100 0 411 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/node -r-xr-xr-x 0 100 0 20 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/main.js diff --git a/js/private/test/image/custom_owner_test_node.listing b/js/private/test/image/custom_owner_test_node.listing index 4cef76254..04ba1409a 100644 --- a/js/private/test/image/custom_owner_test_node.listing +++ b/js/private/test/image/custom_owner_test_node.listing @@ -7,8 +7,9 @@ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runf drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/ --r-xr-xr-x 0 100 0 34656 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs --r-xr-xr-x 0 100 0 1460 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs +-r-xr-xr-x 0 100 0 35421 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 100 0 6009 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs_stat.cjs +-r-xr-xr-x 0 100 0 1631 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/ diff --git a/js/private/test/image/default_test_app.listing b/js/private/test/image/default_test_app.listing index 785009224..027bded2f 100644 --- a/js/private/test/image/default_test_app.listing +++ b/js/private/test/image/default_test_app.listing @@ -2,7 +2,7 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/ --r-xr-xr-x 0 0 0 23981 Jan 1 1970 ./js/private/test/image/bin +-r-xr-xr-x 0 0 0 24025 Jan 1 1970 ./js/private/test/image/bin drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/examples/ @@ -16,7 +16,7 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runf drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/ --r-xr-xr-x 0 0 0 23981 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin +-r-xr-xr-x 0 0 0 24025 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/ --r-xr-xr-x 0 0 0 133 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/node +-r-xr-xr-x 0 0 0 411 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/node -r-xr-xr-x 0 0 0 20 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/main.js diff --git a/js/private/test/image/default_test_node.listing b/js/private/test/image/default_test_node.listing index 3ebfdf82b..1bb171f9f 100644 --- a/js/private/test/image/default_test_node.listing +++ b/js/private/test/image/default_test_node.listing @@ -7,8 +7,9 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runf drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/ --r-xr-xr-x 0 0 0 34656 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs --r-xr-xr-x 0 0 0 1460 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs +-r-xr-xr-x 0 0 0 35421 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 0 0 6009 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs_stat.cjs +-r-xr-xr-x 0 0 0 1631 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/ diff --git a/js/private/test/image/non_ascii/custom_layer_groups_test_app.listing b/js/private/test/image/non_ascii/custom_layer_groups_test_app.listing index 46846288d..2d7622b50 100644 --- a/js/private/test/image/non_ascii/custom_layer_groups_test_app.listing +++ b/js/private/test/image/non_ascii/custom_layer_groups_test_app.listing @@ -4,7 +4,7 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/ --r-xr-xr-x 0 0 0 24076 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2 +-r-xr-xr-x 0 0 0 24120 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2 drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/ @@ -14,8 +14,8 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/ -r-xr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/ㅑㅕㅣㅇ.ㄴㅅ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_/ --r-xr-xr-x 0 0 0 24076 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_/bin2 +-r-xr-xr-x 0 0 0 24120 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_/bin2 drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_node_bin/ --r-xr-xr-x 0 0 0 133 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_node_bin/node +-r-xr-xr-x 0 0 0 411 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_node_bin/node -r-xr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/empty empty.ㄴㅅ -r-xr-xr-x 0 0 0 20 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/main.js diff --git a/js/private/test/image/non_ascii/custom_layer_groups_test_just_the_fs_patch.listing b/js/private/test/image/non_ascii/custom_layer_groups_test_just_the_fs_patch.listing index 501708ac7..99fcdb3ef 100644 --- a/js/private/test/image/non_ascii/custom_layer_groups_test_just_the_fs_patch.listing +++ b/js/private/test/image/non_ascii/custom_layer_groups_test_just_the_fs_patch.listing @@ -9,4 +9,4 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node-patches/ --r-xr-xr-x 0 0 0 34656 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 0 0 35421 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node-patches/fs.cjs diff --git a/js/private/test/image/non_ascii/custom_layer_groups_test_node.listing b/js/private/test/image/non_ascii/custom_layer_groups_test_node.listing index 480acc973..0e23f0435 100644 --- a/js/private/test/image/non_ascii/custom_layer_groups_test_node.listing +++ b/js/private/test/image/non_ascii/custom_layer_groups_test_node.listing @@ -9,7 +9,8 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node-patches/ --r-xr-xr-x 0 0 0 1460 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node-patches/register.cjs +-r-xr-xr-x 0 0 0 6009 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node-patches/fs_stat.cjs +-r-xr-xr-x 0 0 0 1631 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node-patches/register.cjs drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/rules_nodejs~~node~nodejs_linux_amd64/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/ diff --git a/js/private/test/image/regex_edge_cases_test_app.listing b/js/private/test/image/regex_edge_cases_test_app.listing index 1743a02cd..c83cda02f 100644 --- a/js/private/test/image/regex_edge_cases_test_app.listing +++ b/js/private/test/image/regex_edge_cases_test_app.listing @@ -3,7 +3,7 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/ --r-xr-xr-x 0 0 0 23981 Jan 1 1970 ./app/js/private/test/image/bin +-r-xr-xr-x 0 0 0 24025 Jan 1 1970 ./app/js/private/test/image/bin drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/examples/ @@ -17,7 +17,7 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin. drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/ --r-xr-xr-x 0 0 0 23981 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin +-r-xr-xr-x 0 0 0 24025 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/ --r-xr-xr-x 0 0 0 133 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/node +-r-xr-xr-x 0 0 0 411 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_node_bin/node -r-xr-xr-x 0 0 0 20 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/test/image/main.js diff --git a/js/private/test/image/regex_edge_cases_test_node.listing b/js/private/test/image/regex_edge_cases_test_node.listing index c789e0f0c..97fed4a65 100644 --- a/js/private/test/image/regex_edge_cases_test_node.listing +++ b/js/private/test/image/regex_edge_cases_test_node.listing @@ -8,8 +8,9 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin. drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/ --r-xr-xr-x 0 0 0 34656 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs --r-xr-xr-x 0 0 0 1460 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs +-r-xr-xr-x 0 0 0 35421 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 0 0 6009 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs_stat.cjs +-r-xr-xr-x 0 0 0 1631 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/register.cjs drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/nodejs/ diff --git a/js/private/test/node-patches/BUILD.bazel b/js/private/test/node-patches/BUILD.bazel index 6f44f9458..60c098be2 100644 --- a/js/private/test/node-patches/BUILD.bazel +++ b/js/private/test/node-patches/BUILD.bazel @@ -81,6 +81,7 @@ babel_bin.babel( "//js/private/node-patches/src:compile", ], entry_point = "copy_entry_{}".format(t), + node_options = ["--expose-internals"], node_toolchain = toolchain, patch_node_fs = False, # Without node patches on for these tests, the program is going to escape the sandbox if it @@ -101,7 +102,8 @@ babel_bin.babel( "//:node_modules/inline-fixtures", "//js/private/node-patches/src:compile", ], - entry_point = t, + entry_point = "copy_entry_{}".format(t) if (t in TESTS) else t, + node_options = ["--expose-internals"], node_toolchain = toolchain, patch_node_fs = False, # Without node patches on for these tests, the program is going to escape the sandbox if it diff --git a/js/private/test/snapshots/launcher.sh b/js/private/test/snapshots/launcher.sh index 106b436e3..a8398c6b8 100644 --- a/js/private/test/snapshots/launcher.sh +++ b/js/private/test/snapshots/launcher.sh @@ -12,6 +12,7 @@ set -o pipefail -o errexit -o nounset +export JS_BINARY__PATCH_NODE_ESM_LOADER="0" export JS_BINARY__BINDIR="bazel-out/k8-fastbuild/bin" export JS_BINARY__COMPILATION_MODE="fastbuild" export JS_BINARY__TARGET_CPU="k8"