Commit c07db8c5c1b for nodejs

commit c07db8c5c1b1b18e1e0e088b83f56922c142c0cf
Author: Jason Zhang <xzha4350@gmail.com>
Date:   Sun Nov 15 15:18:02 2026 +1030

    src: avoid aborting on filesystem errors in node --run

    `node --run` called the throwing overloads of
    `std::filesystem::is_directory()` and `std::filesystem::current_path()`.
    An unreadable `node_modules` directory or a removed working directory
    made them throw, and the uncaught exception aborted the process.

    Use the `std::error_code` overloads instead. A `node_modules/.bin` that
    cannot be inspected is skipped, and a missing working directory is
    reported as a regular error.

    Signed-off-by: jazelly <xzha4350@gmail.com>
    PR-URL: https://github.com/nodejs/node/pull/67270
    Fixes: https://github.com/nodejs/node/issues/67269
    Reviewed-By: James M Snell <jasnell@gmail.com>
    Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
    Reviewed-By: Jake Yuesong Li <jake.yuesong@gmail.com>

diff --git a/src/node_task_runner.cc b/src/node_task_runner.cc
index 136dec24fa0..b9716846abf 100644
--- a/src/node_task_runner.cc
+++ b/src/node_task_runner.cc
@@ -233,7 +233,9 @@ FindPackageJson(const std::filesystem::path& cwd) {
        directory_path = directory_path.parent_path()) {
     // Append "path/node_modules/.bin" to the env var, if it is a directory.
     auto node_modules_bin = directory_path / "node_modules" / ".bin";
-    if (std::filesystem::is_directory(node_modules_bin)) {
+    // Skip directories that can't be inspected (e.g. permission denied).
+    std::error_code error_code;
+    if (std::filesystem::is_directory(node_modules_bin, error_code)) {
       path_env_var += ConvertPathToUTF8(node_modules_bin) + env_var_separator;
     }

@@ -279,7 +281,15 @@ static void PrintScripts(FILE* out,
 void RunTask(const std::shared_ptr<InitializationResultImpl>& result,
              std::string_view command_id,
              const std::vector<std::string_view>& positional_args) {
-  auto cwd = std::filesystem::current_path();
+  std::error_code error_code;
+  auto cwd = std::filesystem::current_path(error_code);
+  if (error_code) {
+    fprintf(stderr,
+            "Can't determine the current working directory: %s\n",
+            error_code.message().c_str());
+    result->exit_code_ = ExitCode::kGenericUserError;
+    return;
+  }
   auto package_json = FindPackageJson(cwd);

   if (!package_json.has_value()) {
diff --git a/test/parallel/test-node-run.js b/test/parallel/test-node-run.js
index 57faa80b9f8..b276c54ca23 100644
--- a/test/parallel/test-node-run.js
+++ b/test/parallel/test-node-run.js
@@ -12,6 +12,8 @@ const fixtures = require('../common/fixtures');
 const tmpdir = require('../common/tmpdir');
 const envSuffix = common.isWindows ? '-windows' : '';

+tmpdir.refresh();
+
 describe('node --run [command]', { concurrency: !process.env.TEST_PARALLEL }, () => {
   it('returns error on non-existent file', async () => {
     const child = await common.spawnPromisified(
@@ -336,4 +338,45 @@ describe('node --run [command]', { concurrency: !process.env.TEST_PARALLEL }, ()
     assert.strictEqual(child.stdout, '');
     assert.strictEqual(child.code, 1);
   });
+
+  it('skips node_modules directories that cannot be inspected', {
+    skip: common.isWindows || process.getuid() === 0,
+  }, async () => {
+    const projectDir = tmpdir.resolve('unreadable-node-modules');
+    const nodeModules = path.join(projectDir, 'node_modules');
+    fs.mkdirSync(nodeModules, { recursive: true });
+    fs.writeFileSync(path.join(projectDir, 'package.json'), JSON.stringify({
+      scripts: { test: 'echo ok' },
+    }));
+    fs.chmodSync(nodeModules, 0o000);
+    try {
+      const child = await common.spawnPromisified(
+        process.execPath,
+        [ '--run', 'test'],
+        { cwd: projectDir },
+      );
+      assert.strictEqual(child.stdout.trim(), 'ok');
+      assert.strictEqual(child.stderr, '');
+      assert.strictEqual(child.code, 0);
+    } finally {
+      fs.chmodSync(nodeModules, 0o755);
+    }
+  });
+
+  it('returns error when the current working directory is removed', {
+    // Windows, SunOS, AIX and IBM i refuse to remove a directory that is in
+    // use as a cwd.
+    skip: common.isWindows || common.isSunOS || common.isAIX || common.isIBMi,
+  }, async () => {
+    const removedDir = tmpdir.resolve('removed-cwd');
+    fs.mkdirSync(removedDir);
+    const child = await common.spawnPromisified(
+      '/bin/sh',
+      [ '-c', 'rmdir "$PWD" && exec "$0" --run test', process.execPath],
+      { cwd: removedDir },
+    );
+    assert.match(child.stderr, /Can't determine the current working directory/);
+    assert.strictEqual(child.stdout, '');
+    assert.strictEqual(child.code, 1);
+  });
 });