diff --git a/.gitattributes b/.gitattributes index c14502a2a..6bf8e63e5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,5 +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/node-patches/fs.cjs linguist-generated=true +js/private/node-patches/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 0752e91fd..e4037a003 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,7 +14,7 @@ bazel_dep(name = "bazel_features", version = "1.9.0") bazel_dep(name = "bazel_lib", version = "3.0.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") bazel_dep(name = "yq.bzl", version = "0.3.2") tel = use_extension("@aspect_tools_telemetry//:extension.bzl", "telemetry") diff --git a/js/private/js_binary.bzl b/js/private/js_binary.bzl index a3469bd8f..6274363c0 100644 --- a/js/private/js_binary.bzl +++ b/js/private/js_binary.bzl @@ -205,6 +205,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 = False, + ), "include_sources": attr.bool( doc = """When True, `sources` from `JsInfo` providers in `data` targets are included in the runfiles of the target.""", default = True, @@ -320,7 +327,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, @@ -566,11 +576,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 f01045cf8..6e280e435 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 // ========================================================================= @@ -256,10 +268,14 @@ function patcher(roots) { // The escape from the root is not mappable back into the root; throw EINVAL return cb(enoent('readlink', args[0])); } - else { + else if (!useInternalLstatPatch) { // The escape from the root is not mappable back into the root; throw EINVAL return cb(einval('readlink', args[0])); } + else { + // TODO: why does this only apply when useInternalLstatPatch is true? + return cb(null, str); + } } const r = path.resolve(path.dirname(resolved), path.relative(path.dirname(str), next)); if (r != resolved && @@ -388,7 +404,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); @@ -558,36 +576,32 @@ function patcher(roots) { readHopLink(maybe, readNextHop); }); } + const symlinkNoThrow = Object.freeze({ + throwIfNoEntry: false, + }); const hopLinkCache = Object.create(null); function readHopLinkSync(p) { if (hopLinkCache[p]) { return hopLinkCache[p]; } let link; - try { - if (origLstatSync(p).isSymbolicLink()) { - link = origReadlinkSync(p); - if (link) { - if (!path.isAbsolute(link)) { - link = path.resolve(path.dirname(p), link); - } - } - else { - link = HOP_NON_LINK; + const pStats = origLstatSync(p, symlinkNoThrow); + if (!pStats) { + link = HOP_NOT_FOUND; + } + else if (pStats.isSymbolicLink()) { + link = origReadlinkSync(p); + if (link) { + if (!path.isAbsolute(link)) { + link = path.resolve(path.dirname(p), link); } } else { link = HOP_NON_LINK; } } - catch (err) { - if (err.code === 'ENOENT') { - // file does not exist - link = HOP_NOT_FOUND; - } - else { - link = HOP_NON_LINK; - } + else { + link = HOP_NON_LINK; } hopLinkCache[p] = link; return link; @@ -761,6 +775,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..279129368 --- /dev/null +++ b/js/private/node-patches/fs_stat.cjs @@ -0,0 +1,135 @@ +"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'); +const internalFsLStat = internalFs.lstat; +class FsInternalStatPatcher { + constructor(escapeFns, guardedReadLink, guardedReadLinkSync, unguardedRealPath, unguardedRealPathSync) { + this.escapeFns = escapeFns; + this.guardedReadLink = guardedReadLink; + this.guardedReadLinkSync = guardedReadLinkSync; + this.unguardedRealPath = unguardedRealPath; + this.unguardedRealPathSync = unguardedRealPathSync; + } + revert() { + internalFs.lstat = internalFsLStat; + } + 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) { + return internalFsLStat.call(internalFs, path, bigint, reqCallback, throwIfNoEntry); + } + if (reqCallback === internalFs.kUsePromises) { + return internalFsLStat.call(internalFs, path, bigint, reqCallback, throwIfNoEntry).then((stats) => { + return new Promise((resolve, reject) => { + statPatcher.eeguardStats(path, bigint, stats, throwIfNoEntry, (err, guardedStats) => { + err ? reject(err) : resolve(guardedStats); + }); + }); + }); + } + else if (reqCallback === undefined) { + const stats = internalFsLStat.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) { + if (!stats) { + if (throwIfNotFound) { + return cb(new Error('ENOENT')); + } + return cb(null, 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 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 internalFsLStat.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 internalFsLStat.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 5362eb5a9..45496229b 100644 --- a/js/private/node-patches/src/fs.cts +++ b/js/private/node-patches/src/fs.cts @@ -15,7 +15,7 @@ * limitations under the License. */ -import type { PathLike, Stats, BigIntStats } from 'fs' +import type { PathLike, Stats, StatSyncOptions, BigIntStats } from 'fs' import type * as FsType from 'fs' import type * as UrlType from 'url' import * as path from 'path' @@ -55,7 +55,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.') } @@ -107,101 +110,125 @@ 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 | null, - stats: Stats | BigIntStats - ) { - 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 Function) - 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 | null, + stats: Stats | BigIntStats + ) { + 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) + } - return guardedReadLink(args[0], guardedReadLinkCb) + args[0] = resolvePathLike(args[0]) - function guardedReadLinkCb(str: string) { - if (str != args[0]) { - // there are one or more hops within the guards so there is nothing more to do + 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 | null, 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 | null, + 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) } - origLstat(...args) - } + fs.lstatSync = function lstatSync( + ...args: Parameters + ) { + const stats = origLstatSync(...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 + } - if (!stats?.isSymbolicLink()) { - // the file is not a symbolic link so there is nothing more to do - return stats - } + args[0] = resolvePathLike(args[0]) - 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 + } - if (!canEscape(args[0])) { - // the file can not escaped the sandbox 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 + } - 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 - } + try { + args[0] = unguardedRealPathSync(args[0]) - 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: any) { - if (err.code === 'ENOENT') { - // broken link 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: any) { + if (err.code === 'ENOENT') { + // broken link so there is nothing more to do + return stats + } + throw err } - throw err } } @@ -309,9 +336,12 @@ export function patcher(roots: string[]): () => void { if (next == undefined) { // The escape from the root is not mappable back into the root; throw EINVAL return cb(enoent('readlink', args[0])) - } else { + } else if (!useInternalLstatPatch) { // The escape from the root is not mappable back into the root; throw EINVAL return cb(einval('readlink', args[0])) + } else { + // TODO: why does this only apply when useInternalLstatPatch is true? + return cb(null, str) } } const r = path.resolve( @@ -478,7 +508,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) @@ -677,6 +709,10 @@ export function patcher(roots: string[]): () => void { }) } + const symlinkNoThrow: StatSyncOptions = Object.freeze({ + throwIfNoEntry: false, + }) + const hopLinkCache = Object.create(null) as { [f: string]: HopResults } function readHopLinkSync(p: string): HopResults { if (hopLinkCache[p]) { @@ -685,26 +721,20 @@ export function patcher(roots: string[]): () => void { let link: HopResults - try { - if (origLstatSync(p).isSymbolicLink()) { - link = origReadlinkSync(p) as string - if (link) { - if (!path.isAbsolute(link)) { - link = path.resolve(path.dirname(p), link) - } - } else { - link = HOP_NON_LINK + const pStats = origLstatSync(p, symlinkNoThrow) + if (!pStats) { + link = HOP_NOT_FOUND + } else if (pStats.isSymbolicLink()) { + link = origReadlinkSync(p) as string + if (link) { + if (!path.isAbsolute(link)) { + link = path.resolve(path.dirname(p), link) } } else { link = HOP_NON_LINK } - } catch (err: any) { - if (err.code === 'ENOENT') { - // file does not exist - link = HOP_NOT_FOUND - } else { - link = HOP_NON_LINK - } + } else { + link = HOP_NON_LINK } hopLinkCache[p] = link @@ -921,6 +951,10 @@ export function patcher(roots: string[]): () => void { unpatchPromises() } + if (unpatchEsm) { + unpatchEsm() + } + // Re-sync the esm modules to revert to the unpatched module. esmModule.syncBuiltinESMExports() } @@ -945,7 +979,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..d955a890a --- /dev/null +++ b/js/private/node-patches/src/fs_stat.cts @@ -0,0 +1,219 @@ +// 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') +const internalFsLStat = internalFs.lstat + +export class FsInternalStatPatcher { + 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 | null, str?: string) => void + ) => void, + private unguardedRealPathSync: (start: string) => string + ) {} + + revert() { + internalFs.lstat = internalFsLStat + } + + 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) { + return internalFsLStat.call( + internalFs, + path, + bigint, + reqCallback, + throwIfNoEntry + ) + } + + if (reqCallback === internalFs.kUsePromises) { + return ( + internalFsLStat.call( + internalFs, + path, + bigint, + reqCallback, + throwIfNoEntry + ) as Promise + ).then((stats) => { + return new Promise((resolve, reject) => { + statPatcher.eeguardStats( + path, + bigint, + stats, + throwIfNoEntry, + (err, guardedStats) => { + err ? reject(err) : resolve(guardedStats as any) + } + ) + }) + }) + } else if (reqCallback === undefined) { + const stats = internalFsLStat.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 | undefined, + throwIfNotFound: boolean, + cb: (err: unknown, stats?: FsInternalModule.InternalStats) => void + ) { + if (!stats) { + if (throwIfNotFound) { + return cb(new Error('ENOENT')) + } + return cb(null, stats) + } + 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 internalFsLStat.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 internalFsLStat.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..24199ab26 --- /dev/null +++ b/js/private/node-patches/src/fs_stat_types.d.cts @@ -0,0 +1,33 @@ +// 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 { + // A random unique symbol to brand the internal stats type. + type InternalStats = { readonly __internalStatsBrandedType: unique symbol }; + + const kUsePromises: unique symbol; + + class FSReqCallback { + constructor(bigint: boolean); + oncomplete: (err: unknown, stats?: InternalStats) => void; + } + + // https://github.com/nodejs/node/blob/f58613a64c8e02b42391952a6e55a330a7607fa7/typings/internalBinding/fs.d.ts#L129-L137 + function lstat( + path: string, + bigint: boolean, + cb: typeof kUsePromises | FSReqCallback | undefined, + throwIfNotFound: boolean, + ): InternalStats | Promise | void; + } + + 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_wrapper.sh b/js/private/node_wrapper.sh index fae2c2a56..fcfec059f 100755 --- a/js/private/node_wrapper.sh +++ b/js/private/node_wrapper.sh @@ -2,4 +2,13 @@ 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. + # TODO: --disable-warning for stop warnings from that + + exec "$JS_BINARY__NODE_BINARY" \ + --expose-internals \ + --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 7029c0493..bfd0c2508 100644 --- a/js/private/test/image/checksum_test.expected +++ b/js/private/test/image/checksum_test.expected @@ -1,4 +1,4 @@ -3fd4581c0ae8774a7d6d4b174802d91d0fcfc1b99d7b438abeac409636114a11 js/private/test/image/cksum_node +6d3413395e3f7299a5983eb643a0e4ce606f6bcb13ca64b59b25982bd9adc0b7 js/private/test/image/cksum_node 052600f3a82ab6a4cc12cab7384971c960f9c589fdbfcf21bca563c36ff7d16e js/private/test/image/cksum_package_store_3p 971f291232f3ab63aff37fb66c96fbf0eddc05ea9564b9673d0d2c9bfe958994 js/private/test/image/cksum_package_store_1p febf95a6d554c9bda3f0515bfd5ef273ac67d31c231d8162beaef8c4b7bc72f3 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 7ebe60575..226688a17 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 34430 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 35651 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 0 0 6104 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 7d944026f..b24a87812 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 xxxxx 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 xxxxx 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 437 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 c3c23c0a9..ddc6c453d 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 34430 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 35651 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 100 0 6104 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 983df884a..f30e3488d 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 xxxxx 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 xxxxx 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 437 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 5f975bbfa..2e00f8baf 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 34430 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 35651 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 0 0 6104 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..41c33c0fe 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 437 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 96cffcc60..eb6734f07 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 34430 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 35651 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..71ca5a884 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 6104 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 48ba14254..0d4efcf2b 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 xxxxx 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 xxxxx 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 437 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 7ebe60575..226688a17 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 34430 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 35651 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs +-r-xr-xr-x 0 0 0 6104 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..5b57583e0 100644 --- a/js/private/test/node-patches/BUILD.bazel +++ b/js/private/test/node-patches/BUILD.bazel @@ -69,52 +69,70 @@ babel_bin.babel( # Basic tests [ [ - # The primary tests, both .js and .mjs [ - js_test( - name = "{}_{}_test".format( - t.replace(".mjs", "").replace(".js", ""), - toolchain_name, - ), - data = [ - "//:node_modules/inline-fixtures", - "//js/private/node-patches/src:compile", - ], - entry_point = "copy_entry_{}".format(t), - node_toolchain = toolchain, - patch_node_fs = False, - # Without node patches on for these tests, the program is going to escape the sandbox if it - # is on since the fs patches are not on for the tests as they are the code under test - tags = ["no-sandbox"], - ) - for t in TESTS + MJS_TESTS - ], + # The primary tests, both .js and .mjs + [ + js_test( + name = "{}_{}{}_test".format( + t.replace(".mjs", "").replace(".js", ""), + toolchain_name, + "_esm" if useEsmPatch else "", + ), + data = [ + "//:node_modules/inline-fixtures", + "//js/private/node-patches/src:compile", + ], + entry_point = "copy_entry_{}".format(t), + env = { + "NODE_PATCHES_TEST_ESM_LOADER": "1" if useEsmPatch else "", + }, + node_options = ["--expose-internals"] if useEsmPatch else [], + node_toolchain = toolchain, + patch_node_esm_loader = False, + patch_node_fs = False, + # Without node patches on for these tests, the program is going to escape the sandbox if it + # is on since the fs patches are not on for the tests as they are the code under test + tags = ["no-sandbox"], + ) + for t in TESTS + MJS_TESTS + ], - # The .cjs tests converted from .mjs - [ - js_test( - name = "{}_{}_cjs_test".format( - t.replace(".cjs", ""), - toolchain_name, - ), - data = [ - "//:node_modules/inline-fixtures", - "//js/private/node-patches/src:compile", - ], - entry_point = t, - node_toolchain = toolchain, - patch_node_fs = False, - # Without node patches on for these tests, the program is going to escape the sandbox if it - # is on since the fs patches are not on for the tests as they are the code under test - tags = ["no-sandbox"], - ) - for t in CJS_TESTS - ], + # The .cjs tests converted from .mjs + [ + js_test( + name = "{}_{}{}_cjs_test".format( + t.replace(".cjs", ""), + toolchain_name, + "_esm" if useEsmPatch else "", + ), + data = [ + "//:node_modules/inline-fixtures", + "//js/private/node-patches/src:compile", + ], + entry_point = "copy_entry_{}".format(t) if (t in TESTS) else t, + env = { + "NODE_PATCHES_TEST_ESM_LOADER": "1" if useEsmPatch else "", + }, + node_options = ["--expose-internals"] if useEsmPatch else [], + node_toolchain = toolchain, + patch_node_esm_loader = False, + patch_node_fs = False, + # Without node patches on for these tests, the program is going to escape the sandbox if it + # is on since the fs patches are not on for the tests as they are the code under test + tags = ["no-sandbox"], + ) + for t in CJS_TESTS + ], + ] + for toolchain_name, toolchain in zip( + TOOLCHAINS_NAMES, + TOOLCHAINS_VERSIONS, + ) + ] + for useEsmPatch in [ + False, + True, ] - for toolchain_name, toolchain in zip( - TOOLCHAINS_NAMES, - TOOLCHAINS_VERSIONS, - ) ] # Node process spawning tests diff --git a/js/private/test/node-patches/lstat.mjs b/js/private/test/node-patches/lstat.mjs index 41d583a0c..e322c385a 100644 --- a/js/private/test/node-patches/lstat.mjs +++ b/js/private/test/node-patches/lstat.mjs @@ -22,6 +22,8 @@ import * as util from 'node:util' import { patcher } from '../../node-patches/src/fs.cjs' +const useEsmPatch = process.env.NODE_PATCHES_TEST_ESM_LOADER === '1' + // We don't want to bring jest into this repo so we just fake the describe and it functions here async function describe(_, fn) { await fn() @@ -45,7 +47,10 @@ describe('testing lstat', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir)]) + const revertPatches = patcher( + [path.join(fixturesDir)], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'a', 'link') assert.ok( @@ -77,10 +82,10 @@ describe('testing lstat', async () => { async (fixturesDir) => { fixturesDir = fs.realpathSync(fixturesDir) - const revertPatches = patcher([ - path.join(fixturesDir), - path.join(fixturesDir, 'a', 'g'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir), path.join(fixturesDir, 'a', 'g')], + useEsmPatch + ) assert.equal( undefined, @@ -108,10 +113,10 @@ describe('testing lstat', async () => { path.join(fixturesDir, 'a', 'g', 'link') ) - const revertPatches = patcher([ - path.join(fixturesDir), - path.join(fixturesDir, 'a', 'g'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir), path.join(fixturesDir, 'a', 'g')], + useEsmPatch + ) console.error('Starting') console.error(fs.readlink.toString()) @@ -151,7 +156,10 @@ describe('testing lstat', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir, 'a')]) + const revertPatches = patcher( + [path.join(fixturesDir, 'a')], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'a', 'link') @@ -222,7 +230,10 @@ describe('testing lstat', async () => { path.join(fixturesDir, 'b', 'link') ) - const revertPatches = patcher([path.join(fixturesDir, 'a')]) + const revertPatches = patcher( + [path.join(fixturesDir, 'a')], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'b', 'link') diff --git a/js/private/test/node-patches/opendir.mjs b/js/private/test/node-patches/opendir.mjs index 6c9b63ad9..d958099cf 100644 --- a/js/private/test/node-patches/opendir.mjs +++ b/js/private/test/node-patches/opendir.mjs @@ -22,6 +22,8 @@ import * as util from 'node:util' import { patcher } from '../../node-patches/src/fs.cjs' +const useEsmPatch = process.env.NODE_PATCHES_TEST_ESM_LOADER === '1' + // We don't want to bring jest into this repo so we just fake the describe and it functions here async function describe(_, fn) { await fn() @@ -45,7 +47,7 @@ describe('testing opendir', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([fixturesDir]) + const revertPatches = patcher([fixturesDir], useEsmPatch) let dir dir = await util.promisify(fs.opendir)( @@ -114,7 +116,10 @@ describe('testing opendir', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir, 'a')]) + const revertPatches = patcher( + [path.join(fixturesDir, 'a')], + useEsmPatch + ) let dir dir = await util.promisify(fs.opendir)( @@ -178,7 +183,10 @@ describe('testing opendir', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir)]) + const revertPatches = patcher( + [path.join(fixturesDir)], + useEsmPatch + ) const dir = await util.promisify(fs.opendir)( path.join(fixturesDir, 'a') @@ -214,7 +222,10 @@ describe('testing opendir', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir, 'a')]) + const revertPatches = patcher( + [path.join(fixturesDir, 'a')], + useEsmPatch + ) const dir = await util.promisify(fs.opendir)( path.join(fixturesDir, 'a') @@ -271,9 +282,10 @@ describe('testing opendir', async () => { path.join(fixturesDir, 'sandbox', 'link') ) - const revertPatches = patcher([ - path.join(fixturesDir, 'sandbox'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir, 'sandbox')], + useEsmPatch + ) let dir dir = await util.promisify(fs.opendir)( diff --git a/js/private/test/node-patches/readdir.mjs b/js/private/test/node-patches/readdir.mjs index 835afa2ac..c62efb1ad 100644 --- a/js/private/test/node-patches/readdir.mjs +++ b/js/private/test/node-patches/readdir.mjs @@ -22,6 +22,8 @@ import * as util from 'node:util' import { patcher } from '../../node-patches/src/fs.cjs' +const useEsmPatch = process.env.NODE_PATCHES_TEST_ESM_LOADER === '1' + // We don't want to bring jest into this repo so we just fake the describe and it functions here async function describe(_, fn) { await fn() @@ -45,7 +47,7 @@ describe('testing readdir', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([fixturesDir]) + const revertPatches = patcher([fixturesDir], useEsmPatch) let dirents = fs.readdirSync(path.join(fixturesDir, 'a'), { withFileTypes: true, @@ -104,7 +106,10 @@ describe('testing readdir', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir, 'a')]) + const revertPatches = patcher( + [path.join(fixturesDir, 'a')], + useEsmPatch + ) console.error('FOO') console.error(fs.readdirSync) @@ -180,9 +185,10 @@ describe('testing readdir', async () => { path.join(fixturesDir, 'sandbox', 'link') ) - const revertPatches = patcher([ - path.join(fixturesDir, 'sandbox'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir, 'sandbox')], + useEsmPatch + ) let dirents = fs.readdirSync( path.join(fixturesDir, 'sandbox'), diff --git a/js/private/test/node-patches/readlink.mjs b/js/private/test/node-patches/readlink.mjs index cfa90f167..5ca75cef1 100644 --- a/js/private/test/node-patches/readlink.mjs +++ b/js/private/test/node-patches/readlink.mjs @@ -22,6 +22,8 @@ import * as util from 'node:util' import { patcher } from '../../node-patches/src/fs.cjs' +const useEsmPatch = process.env.NODE_PATCHES_TEST_ESM_LOADER === '1' + // We don't want to bring jest into this repo so we just fake the describe and it functions here async function describe(_, fn) { await fn() @@ -45,7 +47,10 @@ describe('testing readlink', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir)]) + const revertPatches = patcher( + [path.join(fixturesDir)], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'a', 'link') @@ -100,7 +105,10 @@ describe('testing readlink', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir, 'a')]) + const revertPatches = patcher( + [path.join(fixturesDir, 'a')], + useEsmPatch + ) const linkPath = path.join( fs.realpathSync(fixturesDir), @@ -160,9 +168,10 @@ describe('testing readlink', async () => { path.join(fixturesDir, 'sandbox', 'link') ) - const revertPatches = patcher([ - path.join(fixturesDir, 'sandbox'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir, 'sandbox')], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'sandbox', 'link') const filePath = path.join(fixturesDir, 'sandbox', 'file') @@ -224,9 +233,10 @@ describe('testing readlink', async () => { path.join(fixturesDir, 'sandbox', 'link') ) - const revertPatches = patcher([ - path.join(fixturesDir, 'sandbox'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir, 'sandbox')], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'sandbox', 'link') const filePath = path.join(fixturesDir, 'sandbox', 'file') diff --git a/js/private/test/node-patches/realpath.mjs b/js/private/test/node-patches/realpath.mjs index de37b435b..8ddb0b5f4 100644 --- a/js/private/test/node-patches/realpath.mjs +++ b/js/private/test/node-patches/realpath.mjs @@ -22,6 +22,8 @@ import * as util from 'node:util' import { patcher } from '../../node-patches/src/fs.cjs' +const useEsmPatch = process.env.NODE_PATCHES_TEST_ESM_LOADER === '1' + // We don't want to bring jest into this repo so we just fake the describe and it functions here async function describe(_, fn) { await fn() @@ -32,7 +34,7 @@ async function it(_, fn) { describe('testing realpath', async () => { await it('can handle empty, dot, undefined & null values', async () => { - const revertPatches = patcher([process.cwd()]) + const revertPatches = patcher([process.cwd()], useEsmPatch) // --------------------------------------------------------------------- // empty string @@ -166,7 +168,10 @@ describe('testing realpath', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir)]) + const revertPatches = patcher( + [path.join(fixturesDir)], + useEsmPatch + ) const linkPath = path.join( fs.realpathSync(fixturesDir), 'a', @@ -217,7 +222,10 @@ describe('testing realpath', async () => { // on mac, inside of bazel, the fixtures dir returned here is not realpath-ed. fixturesDir = fs.realpathSync(fixturesDir) - const revertPatches = patcher([path.join(fixturesDir)]) + const revertPatches = patcher( + [path.join(fixturesDir)], + useEsmPatch + ) const filePath = path.join( fs.realpathSync(fixturesDir), 'a', @@ -310,7 +318,10 @@ describe('testing realpath', async () => { path.join(fixturesDir, 'a', 'link') ) - const revertPatches = patcher([path.join(fixturesDir, 'a')]) + const revertPatches = patcher( + [path.join(fixturesDir, 'a')], + useEsmPatch + ) const linkPath = path.join( fs.realpathSync(fixturesDir), 'a', @@ -492,9 +503,10 @@ describe('testing realpath', async () => { path.join(fixturesDir, 'sandbox', 'link') ) - const revertPatches = patcher([ - path.join(fixturesDir, 'sandbox'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir, 'sandbox')], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'sandbox', 'link') assert.deepStrictEqual( @@ -571,9 +583,10 @@ describe('testing realpath', async () => { path.join(fixturesDir, 'sandbox', 'link') ) - const revertPatches = patcher([ - path.join(fixturesDir, 'sandbox'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir, 'sandbox')], + useEsmPatch + ) const linkPath = path.join(fixturesDir, 'sandbox', 'link') assert.throws(() => { @@ -663,9 +676,10 @@ describe('testing realpath', async () => { path.join(fixturesDir, 'sandbox', 'node_modules', 'pkg') ) - const revertPatches = patcher([ - path.join(fixturesDir, 'sandbox'), - ]) + const revertPatches = patcher( + [path.join(fixturesDir, 'sandbox')], + useEsmPatch + ) const linkPath = path.join( fixturesDir, 'sandbox', diff --git a/js/private/test/node-patches/spawn.js b/js/private/test/node-patches/spawn.js index 24a54d519..e343b9479 100644 --- a/js/private/test/node-patches/spawn.js +++ b/js/private/test/node-patches/spawn.js @@ -16,7 +16,12 @@ async function it(_, fn) { describe('child_process node path', async () => { function assertNodePath({ stdout, stderr, code, error }) { // Process errors - if (stderr?.toString().trim()) { + if ( + stderr + ?.toString() + .replace(/.*internal\/test\/binding.*\.\n.*/, '') + .trim() + ) { throw new Error(`Received stderr: ${stderr.toString()}`) } else if (code) { throw new Error(`Exit code: ${code}`) @@ -128,7 +133,12 @@ describe('child_process node path', async () => { describe('child_process patch depth', async () => { function assertPatchDepth({ stdout, stderr, code, error }) { // Process errors - if (stderr?.toString().trim()) { + if ( + stderr + ?.toString() + .replace(/.*internal\/test\/binding.*\.\n.*/, '') + .trim() + ) { throw new Error(`Received stderr: ${stderr.toString()}`) } else if (code) { throw new Error(`Exit code: ${code}`) 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"