Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENAMETOOLONG error when running prettier #16115

Open
Anu48 opened this issue Feb 23, 2024 · 4 comments · May be fixed by #16233
Open

ENAMETOOLONG error when running prettier #16115

Anu48 opened this issue Feb 23, 2024 · 4 comments · May be fixed by #16233
Labels
area:cli Issues with Prettier's Command Line Interface help wanted We're a small group who can't get to every issue promptly. We’d appreciate help fixing this issue! type:bug Issues identifying ugly output, or a defect in the program

Comments

@Anu48
Copy link

Anu48 commented Feb 23, 2024

Environments:

  • Prettier Version: 3.2.5
  • Running Prettier via:
  • Runtime:
  • Operating System:
  • Prettier plugins (if any):

Steps to reproduce:
See the following action run: https://github.com/Anu48/prettier-enametoolong/actions/runs/8021903284/job/21915048683

We create a couple of files where each file name doubles in character.
Keep a close eye on the 'Prettier (7)' step vs 'Prettier (8)' step.
'Prettier (7)' and Prettier (8)' steps runs yarn prettier. But 'Prettier (7)' step runs yarn prettier on the first 7 files and 'Prettier (8)' step runs yarn prettier on the first 8 files.
Look into the github summary step to see how 'Prettier (7)' step and 'Prettier (8)' processes the files.

Expected behavior:
Running prettier on a spec containing the first 8 files should result to the same behaviour as running prettier on a spec containing 7 files

Actual behavior:
When running prettier on a spec containing 8 files, it results to an ENAMETOOLONG error. It should be consistent with the behaviour found when running prettier with 7 files

@fisker fisker added type:bug Issues identifying ugly output, or a defect in the program help wanted We're a small group who can't get to every issue promptly. We’d appreciate help fixing this issue! area:cli Issues with Prettier's Command Line Interface labels Feb 24, 2024
@npiyush97
Copy link

npiyush97 commented Mar 9, 2024

@setharnold
Copy link

setharnold commented Apr 25, 2024

The error message that's in the logs:

Error: ENAMETOOLONG: name too long, lstat '/home/runner/work/prettier-enametoolong/prettier-enametoolong/{a.json,aa.json,aaaa.json,aaaaaaaa.json,aaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json}'

This suggests that someone tried to use a shell glob syntax in a setting that doesn't automatically expand shell globs.

Syntax like cat /etc/{issue,passwd} works at the shell because it will expand this to the array:

  • argv[0] - cat
  • argv[1] - /etc/issue
  • argv[2] - /etc/passwd
  • argv[3] - NULL

This action appears to be executing something like:

  • argv[0] - prettier
  • argv[1] - /home/ru.../{a.json,aa.json,aa...aaaa.json}

This is a single file, where the file name is around 304 characters long (way too long for most filesystems) and the full path name is around 367 characters (which should be fine). It does not identify seven files or eight files. It identifies one file with a name so long that it cannot exist. (If the first form actually works, then this suggests that prettier may also have a bug that allows it to treat a single argument in a context that turns into multiple arguments. If so, this is usually considered a security problem. What happens if you have a file named $(id > /tmp/owned).json?)

This line in the workflow file looks very suspect:

          spec="{"$(ls a* | head -${{env.count}} |xargs| tr " " ,)"}"
  • ls should never be used to construct paths for further execution like this, it is too prone to errors with files with embedded whitespace or control characters or shell metacharacters.
  • xargs with no parameters is very weird.
  • tr here is so very confusing.
  • executing data as commands via $() is very dangerous. At best it is prone to give you brittle constructions. $() has a place in shell scripts but whatever this is, isn't it.

I strongly recommend that this workflow file, in its current form, should be abandoned. Re-write the file without using ls. See http://mywiki.wooledge.org/ParsingLs for details.

Thanks

@jsoref
Copy link

jsoref commented Apr 25, 2024

If the first form actually works, then this suggests that prettier may also have a bug

The first form worked for the smaller glob as seen in the earlier step (Run prettier (7))

The ls is to show that the glob is actually perfectly tolerable for a shell.

Here's prettier w/ 7:
https://github.com/check-spelling-sandbox/prettier-enametoolong/actions/runs/8840198001/job/24275054917#step:4:5602

Thu, 25 Apr 2024 22:26:33 GMT
[pid  1720] statx(AT_FDCWD, "/home/runner/work/prettier-enametoolong/prettier-enametoolong/{a.json,aa.json,aaaa.json,aaaaaaaa.json,aaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json}", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL,  <unfinished ...>
Thu, 25 Apr 2024 22:26:33 GMT
[pid  1710] epoll_wait(13,  <unfinished ...>
Thu, 25 Apr 2024 22:26:33 GMT
[pid  1720] <... statx resumed>0x7fb38effdb40) = -1 ENOENT (No such file or directory)

Here's prettier w/ 8:
https://github.com/check-spelling-sandbox/prettier-enametoolong/actions/runs/8840198001/job/24275054917#step:5:5630

Thu, 25 Apr 2024 22:26:34 GMT
[pid  1766] statx(AT_FDCWD, "/home/runner/work/prettier-enametoolong/prettier-enametoolong/{a.json,aa.json,aaaa.json,aaaaaaaa.json,aaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json}", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL,  <unfinished ...>
Thu, 25 Apr 2024 22:26:34 GMT
[pid  1756] epoll_wait(13,  <unfinished ...>
Thu, 25 Apr 2024 22:26:34 GMT
[pid  1766] <... statx resumed>0x7f5358ffbb40) = -1 ENAMETOOLONG (File name too long)

Let's talk about what prettier is actually doing to get here...

for await (const { error, filename, ignoreUnknown } of expandPatterns(

which calls

for await (const { filePath, ignoreUnknown, error } of expandPatternsInternal(

which calls lstatSafe:
const stat = await lstatSafe(absolutePath);

which calls lstat:
async function lstatSafe(filePath) {
try {
return await fs.lstat(filePath);
} catch (/** @type {any} */ error) {
/* c8 ignore next 3 */
if (error.code !== "ENOENT") {
throw error;

Here's a simple node script derived from prettier's lstat code to show what's happening:

> const { default: fs } = await import("node:fs/promises");
> await fs.lstat('/tmp/x/a.json')
Stats {
  dev: 41,
  mode: 33188,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: 4096,
  ino: 1732665,
  size: 0,
  blocks: 0,
  atimeMs: 1714085698490.0015,
  mtimeMs: 1714085698490.0015,
  ctimeMs: 1714085698490.0015,
  birthtimeMs: 1714085698490.0015,
  atime: 2024-04-25T22:54:58.490Z,
  mtime: 2024-04-25T22:54:58.490Z,
  ctime: 2024-04-25T22:54:58.490Z,
  birthtime: 2024-04-25T22:54:58.490Z
}
> await fs.lstat('/tmp/x/{a.json,aa.json}')
Uncaught Error: ENOENT: no such file or directory, lstat '/tmp/x/{a.json,aa.json}'
    at async Object.lstat (node:internal/fs/promises:1018:18)
    at async REPL24:1:33 {
  errno: -2,
  code: 'ENOENT',
  syscall: 'lstat',
  path: '/tmp/x/{a.json,aa.json}'
}
> await fs.lstat('/tmp/x/{a.json,aa.json,aaaa.json,aaaaaaaa.json,aaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json}')
Uncaught:
Error: ENAMETOOLONG: name too long, lstat '/tmp/x/{a.json,aa.json,aaaa.json,aaaaaaaa.json,aaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json}'
    at async Object.lstat (node:internal/fs/promises:1018:18)
    at async REPL25:1:33 {
  errno: -36,
  code: 'ENAMETOOLONG',
  syscall: 'lstat',
  path: '/tmp/x/{a.json,aa.json,aaaa.json,aaaaaaaa.json,aaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json}'
}

As we can see from the basic node calls, ENAMETOOLONG is just another error state from lstat which functionally means there is no file by that name on the disk that the file system is willing to offer -- that's equivalent to the ENOENT case currently handled.

So, all we need to do to make this code friendlier is to handle it in the same manner.

@setharnold
Copy link

Well, I'm very glad that prettier isn't going through the shell to do this, but it seems very strange and unusual to me all the same. But, if handling arguments as globs transparently is actually a desired feature, then yeah, it'd be worth handling ENAMETOOLONG similarly to ENOENT.

@jsoref jsoref linked a pull request Apr 26, 2024 that will close this issue
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:cli Issues with Prettier's Command Line Interface help wanted We're a small group who can't get to every issue promptly. We’d appreciate help fixing this issue! type:bug Issues identifying ugly output, or a defect in the program
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants