Skip to content

Commit 927352b

Browse files
fix(node/fs): add missing stat path argument validation (#27086)
We didn't validate the `path` argument that's passed to `fs.stat()` and `fs.statSync()` which lead to wrong errors being thrown. The `@rollup/plugin-node-resolve` code calls it with `undefined` quite a lot which lead to `nitro` and `nuxt` failing. Fixes #26700 --------- Co-authored-by: Yoshiya Hinosawa <[email protected]>
1 parent 42b71d8 commit 927352b

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

ext/node/polyfills/_fs/_fs_stat.ts

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { denoErrorToNodeError } from "ext:deno_node/internal/errors.ts";
77
import { promisify } from "ext:deno_node/internal/util.mjs";
88
import { primordials } from "ext:core/mod.js";
9+
import { getValidatedPath } from "ext:deno_node/internal/fs/utils.mjs";
910

1011
const { ObjectCreate, ObjectAssign } = primordials;
1112

@@ -379,6 +380,7 @@ export function stat(
379380
? optionsOrCallback
380381
: { bigint: false };
381382

383+
path = getValidatedPath(path).toString();
382384
if (!callback) throw new Error("No callback function supplied");
383385

384386
Deno.stat(path).then(
@@ -409,6 +411,8 @@ export function statSync(
409411
path: string | URL,
410412
options: statOptions = { bigint: false, throwIfNoEntry: true },
411413
): Stats | BigIntStats | undefined {
414+
path = getValidatedPath(path).toString();
415+
412416
try {
413417
const origin = Deno.statSync(path);
414418
return CFISBIS(origin, options.bigint);

tests/unit_node/_fs/_fs_stat_test.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
22
import { assertCallbackErrorUncaught } from "../_test_utils.ts";
33
import { BigIntStats, stat, Stats, statSync } from "node:fs";
4-
import { assertEquals, fail } from "@std/assert";
4+
import { assert, assertEquals, fail } from "@std/assert";
55

66
export function assertStats(actual: Stats, expected: Deno.FileInfo) {
77
assertEquals(actual.dev, expected.dev);
@@ -152,3 +152,38 @@ Deno.test({
152152
assertEquals(stats.isSocket(), false);
153153
},
154154
});
155+
156+
Deno.test({
157+
name: "[node/fs] stat invalid path error",
158+
async fn() {
159+
try {
160+
await new Promise<Stats>((resolve, reject) => {
161+
stat(
162+
// deno-lint-ignore no-explicit-any
163+
undefined as any,
164+
(err, stats) => err ? reject(err) : resolve(stats),
165+
);
166+
});
167+
fail();
168+
} catch (err) {
169+
assert(err instanceof TypeError);
170+
// deno-lint-ignore no-explicit-any
171+
assertEquals((err as any).code, "ERR_INVALID_ARG_TYPE");
172+
}
173+
},
174+
});
175+
176+
Deno.test({
177+
name: "[node/fs] statSync invalid path error",
178+
fn() {
179+
try {
180+
// deno-lint-ignore no-explicit-any
181+
statSync(undefined as any);
182+
fail();
183+
} catch (err) {
184+
assert(err instanceof TypeError);
185+
// deno-lint-ignore no-explicit-any
186+
assertEquals((err as any).code, "ERR_INVALID_ARG_TYPE");
187+
}
188+
},
189+
});

tests/unit_node/fs_test.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/// <reference lib="deno.ns" />
44
import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert";
55
import { join } from "node:path";
6+
import { fileURLToPath } from "node:url";
67
import { tmpdir } from "node:os";
78
import {
89
closeSync,
@@ -160,7 +161,9 @@ Deno.test(
160161
} catch (error: unknown) {
161162
assertEquals(
162163
`${error}`,
163-
`Error: ENOENT: no such file or directory, stat '${fileUrl.pathname}'`,
164+
`Error: ENOENT: no such file or directory, stat '${
165+
fileURLToPath(fileUrl)
166+
}'`,
164167
);
165168
}
166169
},

0 commit comments

Comments
 (0)