Skip to content

Mac Installer: npm and npx created with wrong permissions #57548

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

Open
sdavids opened this issue Mar 19, 2025 · 9 comments
Open

Mac Installer: npm and npx created with wrong permissions #57548

sdavids opened this issue Mar 19, 2025 · 9 comments

Comments

@sdavids
Copy link

sdavids commented Mar 19, 2025

Version

22.14.0

Platform

Darwin redacted 21.6.0 Darwin Kernel Version 21.6.0: Mon Jun 24 00:56:10 PDT 2024; root:xnu-8020.240.18.709.2~1/RELEASE_X86_64 x86_64

What steps will reproduce the bug?

Set a custom umask in macOS - Apple Support

$ printf "umask 077\n" >> ~/.zshrc
$ sudo launchctl config user umask 077
$ sudo reboot   

Verify

$ grep -A1 Umask /private/var/db/com.apple.xpc.launchd/config/user.plist
	<key>Umask</key>
	<integer>63</integer>
$ umask
077
$ sudo umask
0077
$ touch /tmp/a
$ ls -la /tmp/a
-rw-------  1 redacted  wheel  0 Mar 19 17:12 /tmp/a
$ sudo touch /tmp/b
$ ls -la /tmp/b
-rw-------  1 root  wheel  0 Mar 19 17:12 /tmp/b
$ sudo rm /tmp/{a,b}

Download the installer:

https://nodejs.org/dist/v22.14.0/node-v22.14.0.pkg

Execute the installer.


$ ls -la /usr/local/bin/{corepack,node,npm,npx}
lrwxr-xr-x  1 root  wheel         45 Mar 19 16:55 /usr/local/bin/corepack -> ../lib/node_modules/corepack/dist/corepack.js
-rwxr-xr-x  1 root  wheel  220243216 Feb 11 05:31 /usr/local/bin/node

ls: /usr/local/bin/npm: Permission denied
lrwx------  1 root  admin         38 Mar 19 16:55 /usr/local/bin/npm

ls: /usr/local/bin/npx: Permission denied
lrwx------  1 root  admin         38 Mar 19 16:55 /usr/local/bin/npx

How often does it reproduce? Is there a required condition?

Always.

What is the expected behavior? Why is that the expected behavior?

The symlinks have the correct permissions: rwxr-xr-x

What do you see instead?

The symlinks have the incorrect permission: rwx------

Additional information

I think all macOS installer versions are affected.

So this might have to be backported…

@sdavids sdavids changed the title Mac Installer: npm and npx symlinks created with wrong permissions Mac Installer: npm and npx created with wrong permissions Mar 20, 2025
@sdavids
Copy link
Author

sdavids commented Mar 20, 2025

This is especially bad because /usr/local is protected by System Integrity Protection.

To fix it one has to:

  1. disable SIP
  2. sudo chmod ugo+rx /usr/local/bin/{npm,npx} in Recovery mode
  3. enable SIP.

@bnoordhuis
Copy link
Member

It's doing exactly what you told it to, right? Don't change your umask if you don't want that.

It's like the old joke about a man who goes to visit his MD. "Doc, it bleeds when I cut myself." "Then stop doing that!"

@sdavids
Copy link
Author

sdavids commented Mar 25, 2025

I am not sure where your hostile attitude comes from, but I hope you are having a nice day.


Currently, the NodeJS installer respects the umask configured for users apps only for npm and npx—for all other files including the node and corepack binaries it does not:

$ ls -la /usr/local/bin/{corepack,node,npm,npx}
lrwxr-xr-x 1 root wheel 45 Mar 19 16:55 /usr/local/bin/corepack -> ../lib/node_modules/corepack/dist/corepack.js
-rwxr-xr-x 1 root wheel 220243216 Feb 11 05:31 /usr/local/bin/node

ls: /usr/local/bin/npm: Permission denied
lrwx------ 1 root admin 38 Mar 19 16:55 /usr/local/bin/npm

ls: /usr/local/bin/npx: Permission denied
lrwx------ 1 root admin 38 Mar 19 16:55 /usr/local/bin/npx

This is inconsistent: It should either respect the umask configured for users apps for all files or none.


Let’s see what the docs say:

Set a custom umask in macOS — Umask for user apps

[…] sets the user's umask for every app they open, including apps they access from the command line and new files that those apps create.

Yes, an installer is an app, so one might argue that it should respect the umask for user apps.

On the other hand, one could argue that an installer is a special kind of app.


The NodeJS installer only supports System-wide installation.

Therefore, the installed binaries should have read and execute permissions for owner, group, and other so that they are accessible System-wide, i.e. by all users.

@sdavids
Copy link
Author

sdavids commented Mar 25, 2025

One can also set the unmask for system processes but that should not be used by an installer either.

@bnoordhuis
Copy link
Member

This is inconsistent: It should either respect the umask configured for users apps for all files or none.

npm, npx and corepack are all created the exact same way.

I suspect you installed multiple versions over each other. Note how the timestamps and group names are different.

Apropos umask: if someone wants to install node and cut out group or other, they should be able to. You don't like what it did to your local install but that's entirely self-inflicted, it's classic PEBKAC.

@sdavids
Copy link
Author

sdavids commented Mar 25, 2025

npm, npx and corepack are all created the exact same way.

They are not.

I suspect you installed multiple versions over each other. Note how the timestamps and group names are different.

No, the installer is responsible.

$ curl -sO https://nodejs.org/dist/v22.14.0/node-v22.14.0.pkg
$ shasum node-v22.14.0.pkg
f1d93dde78a2353afa56d750a4a205172d91606c  node-v22.14.0.pkg
$ pkgutil --expand node-v22.14.0.pkg node22
$ tree -I Distribution -I Resources --noreport node22
node22
├── node-v22.14.0.pkg
│   ├── Bom
│   ├── PackageInfo
│   └── Payload
└── npm-v10.9.2.pkg
    ├── Bom
    ├── PackageInfo
    ├── Payload
    └── Scripts
        ├── postinstall
        └── preinstall
$ cat node22/npm-v10.9.2.pkg/Scripts/postinstall
#!/bin/sh

cd /usr/local/bin || exit 1
ln -sf ../lib/node_modules/npm/bin/npm-cli.js npm
ln -sf ../lib/node_modules/npm/bin/npx-cli.js npx
$ tar -tvf node22/node-v22.14.0.pkg/Payload | grep 'bin/node'
-rwxr-xr-x  1 0      0   220243216 Feb 11 05:31 ./usr/local/bin/node
$ tar -tvf node22/npm-v10.9.2.pkg/Payload | grep 'npm-cli'
-rwxr-xr-x  1 0      0          54 Oct  8 09:19 ./usr/local/lib/node_modules/npm/bin/npm-cli.js
$ tar -tvf node22/npm-v10.9.2.pkg/Payload | grep 'npx-cli'
-rwxr-xr-x  1 0      0        2921 Oct  8 09:20 ./usr/local/lib/node_modules/npm/bin/npx-cli.js

All files are copied with the correct permissions, but the postinstall script creates the symlinks with the wrong permissions.

That is why everything is fine exept for the npm and npx symlinks.


A fix would be:

#!/bin/sh

cd /usr/local/bin || exit 1
umask 022
ln -sf ../lib/node_modules/npm/bin/npm-cli.js npm
ln -sf ../lib/node_modules/npm/bin/npx-cli.js npx

@aduh95
Copy link
Contributor

aduh95 commented Mar 29, 2025

I think it's worth asking the question whether this postinstall script is still needed, if the installer installs corepack correctly without the postinstall script, chances are it would also install npm and npx correctly.

@sdavids
Copy link
Author

sdavids commented Mar 30, 2025

$ tar -tvf node22/node-v22.14.0.pkg/Payload | grep './usr/local/bin'
drwxr-xr-x  4 0      0           0 Feb 11 05:31 ./usr/local/bin
-rwxr-xr-x  1 0      0   220243216 Feb 11 05:31 ./usr/local/bin/node
lrwxr-xr-x  1 0      0          45 Feb 11 05:31 ./usr/local/bin/corepack -> ../lib/node_modules/corepack/dist/corepack.js

corepack is part of the node pkg whereas the npm and npx symlinks are created via the postinstall script.

	unlink $(MACOSOUTDIR)/dist/node/usr/local/bin/npm
	unlink $(MACOSOUTDIR)/dist/node/usr/local/bin/npx
	$(NODE) tools/license2rtf.mjs < LICENSE > \
		$(MACOSOUTDIR)/installer/productbuild/Resources/license.rtf
	cp doc/osx_installer_logo.png $(MACOSOUTDIR)/installer/productbuild/Resources
	pkgbuild --version $(FULLVERSION) \
		--identifier org.nodejs.node.pkg \
		--root $(MACOSOUTDIR)/dist/node $(MACOSOUTDIR)/pkgs/node-$(FULLVERSION).pkg
	pkgbuild --version $(NPMVERSION) \
		--identifier org.nodejs.npm.pkg \
		--scripts ./tools/macos-installer/pkgbuild/npm/scripts \
		--root $(MACOSOUTDIR)/dist/npm \
			$(MACOSOUTDIR)/pkgs/npm-$(NPMVERSION).pkg

Notice the unlink, maybe it should be changed to:

	pkgbuild --version $(FULLVERSION) \
		--identifier org.nodejs.node.pkg \
		--root $(MACOSOUTDIR)/dist/node $(MACOSOUTDIR)/pkgs/node-$(FULLVERSION).pkg
// add the two links again here
	pkgbuild --version $(NPMVERSION) \
		--identifier org.nodejs.npm.pkg \
		--scripts ./tools/macos-installer/pkgbuild/npm/scripts \
		--root $(MACOSOUTDIR)/dist/npm \
			$(MACOSOUTDIR)/pkgs/npm-$(NPMVERSION).pkg

sdavids added a commit to sdavids/node that referenced this issue Mar 30, 2025
@sdavids
Copy link
Author

sdavids commented Mar 30, 2025

see #57661 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants