Skip to content

Commit 37c960d

Browse files
committed
wip
1 parent 063d7e8 commit 37c960d

10 files changed

+173
-158
lines changed

js/private/node-patches/fs.cjs

Lines changed: 76 additions & 68 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/private/node-patches/src/fs.cts

Lines changed: 83 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ const PATCHED_FS_METHODS: ReadonlyArray<keyof typeof FsType> = [
5353

5454
/**
5555
* Function that patches the `fs` module to not escape the given roots.
56+
*
57+
* NOTE: internally Node may call back to `fs.*` methods for example
58+
* realpath: https://github.com/nodejs/node/blob/v24.12.0/lib/fs.js#L2927
59+
* https://github.com/nodejs/node/blob/v24.12.0/lib/fs.js#L2951-L2957
60+
*
61+
* writeFile: https://github.com/nodejs/node/blob/v24.12.0/lib/fs.js#L2372
62+
*
63+
* ... many other places.
64+
*
65+
* However in other scenarios such as ESM module resolution it uses internal invocations
66+
* that can not be patched via `fs.*` methods.
67+
*
5668
* @returns a function to undo the patches.
5769
*/
5870
export function patcher(
@@ -132,103 +144,98 @@ export function patcher(
132144
// fs.lstat
133145
// =========================================================================
134146

135-
if (!useInternalLstatPatch) {
136-
fs.lstat = function lstat(...args: Parameters<typeof FsType.lstat>) {
137-
// preserve error when calling function without required callback
138-
if (typeof args[args.length - 1] !== 'function') {
139-
return origLstat(...args)
140-
}
147+
fs.lstat = function lstat(...args: Parameters<typeof FsType.lstat>) {
148+
// preserve error when calling function without required callback
149+
if (typeof args[args.length - 1] !== 'function') {
150+
return origLstat(...args)
151+
}
141152

142-
const cb = once(args[args.length - 1] as Function)
153+
const cb = once(args[args.length - 1] as Function)
143154

144-
// override the callback
145-
args[args.length - 1] = function lstatCb(
146-
err: Error | null,
147-
stats: Stats | BigIntStats
148-
) {
149-
if (err) return cb(err)
155+
// override the callback
156+
args[args.length - 1] = function lstatCb(
157+
err: Error | null,
158+
stats: Stats | BigIntStats
159+
) {
160+
if (err) return cb(err)
150161

151-
if (!stats.isSymbolicLink()) {
152-
// the file is not a symbolic link so there is nothing more to do
153-
return cb(null, stats)
154-
}
162+
if (!stats.isSymbolicLink()) {
163+
// the file is not a symbolic link so there is nothing more to do
164+
return cb(null, stats)
165+
}
155166

156-
args[0] = resolvePathLike(args[0])
167+
args[0] = resolvePathLike(args[0])
157168

158-
if (!canEscape(args[0])) {
159-
// the file can not escaped the sandbox so there is nothing more to do
160-
return cb(null, stats)
161-
}
169+
if (!canEscape(args[0])) {
170+
// the file can not escaped the sandbox so there is nothing more to do
171+
return cb(null, stats)
172+
}
162173

163-
return guardedReadLink(args[0], guardedReadLinkCb)
174+
return guardedReadLink(args[0], guardedReadLinkCb)
164175

165-
function guardedReadLinkCb(str: string) {
166-
if (str != args[0]) {
167-
// there are one or more hops within the guards so there is nothing more to do
168-
return cb(null, stats)
169-
}
176+
function guardedReadLinkCb(str: string) {
177+
if (str != args[0]) {
178+
// there are one or more hops within the guards so there is nothing more to do
179+
return cb(null, stats)
180+
}
170181

171-
// there are no hops so lets report the stats of the real file;
172-
// we can't use origRealPath here since that function calls lstat internally
173-
// which can result in an infinite loop
174-
return unguardedRealPath(args[0], unguardedRealPathCb)
182+
// there are no hops so lets report the stats of the real file;
183+
// we can't use origRealPath here since that function calls lstat internally
184+
// which can result in an infinite loop
185+
return unguardedRealPath(args[0], unguardedRealPathCb)
175186

176-
function unguardedRealPathCb(
177-
err: Error | null,
178-
str?: string
179-
) {
180-
if (err) {
181-
if ((err as any).code === 'ENOENT') {
182-
// broken link so there is nothing more to do
183-
return cb(null, stats)
184-
}
185-
return cb(err)
187+
function unguardedRealPathCb(err: Error | null, str?: string) {
188+
if (err) {
189+
if ((err as any).code === 'ENOENT') {
190+
// broken link so there is nothing more to do
191+
return cb(null, stats)
186192
}
187-
return origLstat(str!, cb)
193+
return cb(err)
188194
}
195+
return origLstat(str!, cb)
189196
}
190197
}
191-
192-
origLstat(...args)
193198
}
194199

195-
fs.lstatSync = function lstatSync(
196-
...args: Parameters<typeof FsType.lstatSync>
197-
) {
198-
const stats = origLstatSync(...args)
200+
origLstat(...args)
201+
}
199202

200-
if (!stats?.isSymbolicLink()) {
201-
// the file is not a symbolic link so there is nothing more to do
202-
return stats
203-
}
203+
fs.lstatSync = function lstatSync(
204+
...args: Parameters<typeof FsType.lstatSync>
205+
) {
206+
const stats = origLstatSync(...args)
204207

205-
args[0] = resolvePathLike(args[0])
208+
if (!stats?.isSymbolicLink()) {
209+
// the file is not a symbolic link so there is nothing more to do
210+
return stats
211+
}
206212

207-
if (!canEscape(args[0])) {
208-
// the file can not escaped the sandbox so there is nothing more to do
209-
return stats
210-
}
213+
args[0] = resolvePathLike(args[0])
211214

212-
const guardedReadLink: string = guardedReadLinkSync(args[0])
213-
if (guardedReadLink != args[0]) {
214-
// there are one or more hops within the guards so there is nothing more to do
215-
return stats
216-
}
215+
if (!canEscape(args[0])) {
216+
// the file can not escaped the sandbox so there is nothing more to do
217+
return stats
218+
}
217219

218-
try {
219-
args[0] = unguardedRealPathSync(args[0])
220+
const guardedReadLink: string = guardedReadLinkSync(args[0])
221+
if (guardedReadLink != args[0]) {
222+
// there are one or more hops within the guards so there is nothing more to do
223+
return stats
224+
}
220225

221-
// there are no hops so lets report the stats of the real file;
222-
// we can't use origRealPathSync here since that function calls lstat internally
223-
// which can result in an infinite loop
224-
return origLstatSync(...args)
225-
} catch (err: any) {
226-
if (err.code === 'ENOENT') {
227-
// broken link so there is nothing more to do
228-
return stats
229-
}
230-
throw err
226+
try {
227+
args[0] = unguardedRealPathSync(args[0])
228+
229+
// there are no hops so lets report the stats of the real file;
230+
// we can't use origRealPathSync here since that function calls lstat internally
231+
// which can result in an infinite loop
232+
return origLstatSync(...args)
233+
} catch (err: any) {
234+
if (err.code === 'ENOENT') {
235+
// broken link so there is nothing more to do
236+
return stats
231237
}
238+
throw err
232239
}
233240
}
234241

@@ -505,9 +512,7 @@ export function patcher(
505512

506513
if (promisePropertyDescriptor) {
507514
const promises: typeof fs.promises = {}
508-
if (!useInternalLstatPatch) {
509-
promises.lstat = util.promisify(fs.lstat)
510-
}
515+
promises.lstat = util.promisify(fs.lstat)
511516
// NOTE: node core uses the newer realpath function fs.promises.native instead of fs.realPath
512517
promises.realpath = util.promisify(fs.realpath.native)
513518
promises.readlink = util.promisify(fs.readlink)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
539b915eaa94b38691b6dfdbb852c747b253ffb579beee3dbb4efff623606200 js/private/test/image/cksum_node.tar
1+
e0707f0a41383142ff40b88b0024f3113de0d6ec3e1676e92f89c2b16883644f js/private/test/image/cksum_node.tar
22
052600f3a82ab6a4cc12cab7384971c960f9c589fdbfcf21bca563c36ff7d16e js/private/test/image/cksum_package_store_3p.tar
33
971f291232f3ab63aff37fb66c96fbf0eddc05ea9564b9673d0d2c9bfe958994 js/private/test/image/cksum_package_store_1p.tar
44
febf95a6d554c9bda3f0515bfd5ef273ac67d31c231d8162beaef8c4b7bc72f3 js/private/test/image/cksum_node_modules.tar

js/private/test/image/custom_layers_nomatch_test_node.listing

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.
88
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/
99
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/
1010
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/
11-
-r-xr-xr-x 0 0 0 35418 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs
12-
-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
11+
-r-xr-xr-x 0 0 0 35525 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs.cjs
12+
-r-xr-xr-x 0 0 0 6282 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/_main/js/private/node-patches/fs_stat.cjs
1313
-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
1414
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/
1515
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/bin.runfiles/rules_nodejs~~node~nodejs_linux_amd64/bin/

0 commit comments

Comments
 (0)