esbuild: Bundling fails without read permission to parent directories

Hi, I am trying to use esbuild on an Ubuntu VPS where my $HOME directory is /usr/home/<USER>, but I don’t have read permission to the parent folder /usr/home.

This causes bundling to fail with Cannot read directory ... permission denied

The error can be reproduced on any machine by mimicking the permissions layout:

sudo mkdir forbidden && sudo mkdir forbidden/repo
sudo chmod o-rw forbidden && sudo chmod a+w forbidden/repo
cd forbidden/repo
npm init -y
npm i esbuild react
echo 'import "react"' | npx esbuild --bundle

Output:

 > error: Cannot read directory "..": permission denied

 > <stdin>:1:7: error: Could not resolve "react" (mark it as external to exclude it from the bundle)

From my (ignorant) viewpoint it seems unnecessary to read anything above the package root when resolving node modules, so I thought esbuild could possibly prevent tripping up in this situation. (E.g. Webpack bundles without errors in the same situation.)

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 18 (12 by maintainers)

Most upvoted comments

Thanks for all the help. Now I finally understand the problem. I think an appropriate fix should be to just treat this directory as empty, since node seems to let you still import packages declared in the parent directory of the inaccessible directory. Treating it as empty means esbuild will simply pass through it on its search for packages. Working on a fix now.

Ha, I just tried the exact same steps, but with chmod 111 .. in the place of chmod 000 .., and then everything works except esbuild 😃

This is fun, I’m learning some things about permissions (no expert here either). chmod 111 means the x (execute) permission is there but nothing else. If xis missing, you can’t even cd into the folder and it seems to break a lot. Makes sense that a VPS like the one mentioned above would give x but not rw to the /home folder.

repro:

$ mkdir -p beep/boop/bop
$ cd beep/boop/bop
$ chmod 000 ..
$ echo 'console.log(1 ? 2 : 3)' > 0.js
$ esbuild --version
0.9.0
$ cat 0.js
console.log(1 ? 2 : 3)
$ esbuild ./0.js --minify
 > error: Cannot read directory "..": permission denied

 > error: Cannot read directory ".": permission denied

 > error: Cannot read directory ".": permission denied

 > error: Could not resolve "./0.js"

4 errors
$ chmod 700 ..
$ esbuild ./0.js --minify
console.log(2);

This causes bundling to fail with Cannot read directory ... permission denied

Thanks for reporting this issue. I’m aware of the issue and it’s come up once before here: https://github.com/evanw/esbuild/issues/803#issuecomment-778613954. I gave fixing it a quick try then but I was unable to reproduce the issue myself (didn’t try too hard at the time) and gave up. Even your repro instructions fail to cause the issue for me. I can give it another shot though.

The underlying reason is that esbuild makes heavy use of caching to avoid huge performance issues with node’s path resolution algorithm. The algorithm is inherently inefficient because it involves making potentially hundreds of file system queries to resolve each individual import path (check all implicit file extensions * check package.json and implicit index files * check node_modules in all parent directories).

My implementation caches file system syscalls in the resolver to try to make this as efficient as possible. The cache in the resolver currently works by ensuring the parent directory is cached before caching a child directory, but that doesn’t work if a parent directory is off-limits. Without investigating this myself first, I’m not totally sure what would need to change about the caching strategy to fix this problem correctly. But I agree that it should be fixed.