Skip to content

[node] - Install pnpm as non-root user to prevent root-owned npm cache#1625

Open
brianhelba wants to merge 2 commits intodevcontainers:mainfrom
brianhelba:node-root
Open

[node] - Install pnpm as non-root user to prevent root-owned npm cache#1625
brianhelba wants to merge 2 commits intodevcontainers:mainfrom
brianhelba:node-root

Conversation

@brianhelba
Copy link
Copy Markdown

Currently, the pnpm installation block runs npm install -g pnpm in a bare subshell as root, unlike every other npm/nvm operation in the script which uses su ${USERNAME}. This causes the npm cache directory to be created owned by root:root, leading to EACCES errors for the non-root user on subsequent npm operations.

This is particularly reproducible on macOS with Rosetta 2 emulation, where the cache directory may not already exist from prior steps.

Note, the explicit setting of proxy env vars (http_proxy, https_proxy, no_proxy) is likely a workaround for npm/cli#6835. No other commands in this script use that workaround anymore, so this change uses the same su syntax as all other commands.

Copy link
Copy Markdown
Contributor

@Kaniska244 Kaniska244 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @brianhelba

Thank you for the contribution. Would you kindly bump the node feature version.

Currently, the pnpm installation block runs `npm install -g pnpm` in
a bare subshell as root, unlike every other npm/nvm operation in the
script which uses `su ${USERNAME}`. This causes the npm cache directory
to be created owned by `root:root`, leading to `EACCES` errors for the
non-root user on subsequent npm operations.

This is particularly reproducible on macOS with Rosetta 2 emulation,
where the cache directory may not already exist from prior steps.

Note, the explicit setting of proxy env vars (`http_proxy`, `https_proxy`,
`no_proxy`) is likely a workaround for npm/cli#6835.
No other commands in this script use that workaround anymore, so this change
uses the same `su` syntax as all other commands.
@brianhelba
Copy link
Copy Markdown
Author

@Kaniska244 Done. This is simply a bugfix and doesn't add or remove features, so I bumped the patch version.

@brianhelba brianhelba requested a review from Kaniska244 April 20, 2026 22:41
Copy link
Copy Markdown
Contributor

@Kaniska244 Kaniska244 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be further reviewed by maintainers.

@Kaniska244
Copy link
Copy Markdown
Contributor

@brianhelba please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"

Contributor License Agreement

Hi @brianhelba

Would you please accept the above license agreement.

@brianhelba
Copy link
Copy Markdown
Author

@Kaniska244 Sorry, I'm awaiting approval from my employer. I'm hoping this will be quick.

@brianhelba
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Kitware, Inc."

@brianhelba
Copy link
Copy Markdown
Author

@Kaniska244 All done.

Comment thread src/node/install.sh
if bash -c ". '${NVM_DIR}/nvm.sh' && type npm >/dev/null 2>&1"; then
(
. "${NVM_DIR}/nvm.sh"
[ ! -z "$http_proxy" ] && npm set proxy="$http_proxy"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Others may need these proxies; could they be added back?

Copy link
Copy Markdown
Author

@brianhelba brianhelba Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my commit message:

Note, the explicit setting of proxy env vars (http_proxy, https_proxy, no_proxy) is likely a workaround for npm/cli#6835. No other commands in this script use that workaround anymore, so this change uses the same su syntax as all other commands.

So, this doesn't change anything for users. They can still set $http_proxy and npm will directly respect it.

Every other install block works this way. The removed code just seems like leftovers of a workaround. If the workaround is actually necessary for some reason, than it's a bug that every other install block lacks it.

Comment thread src/node/install.sh
[ ! -z "$no_proxy" ] && npm set noproxy="$no_proxy"
npm install -g pnpm@$PNPM_VERSION --force
)
su ${USERNAME} -c "umask 0002 && . '${NVM_DIR}/nvm.sh' && npm install -g pnpm@${PNPM_VERSION} --force"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any implications for switching to user-based vs root based? There may have been assumptions made by others based on this being root-based. If it could cause issues, we may want to consider adding an option and defaulting to the original approach.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every other install block installs things as the user USERNAME, instead of root. I am not aware of any reason that pnpm's files should be owned by root, but everything else should be owned by USERNAME.

Further, this doesn't just cause pnpm-specific things to be owned by root, it can impact other common Node directories if this block runs first. From my commit message:

This causes the npm cache directory to be created owned by root:root, leading to EACCES errors for the non-root user on subsequent npm operations.

The unconditional ownership of some files by root is the essence of the bug. Features are (I believe) supposed to respect the active user (and every other part of this script does). A flag would be a "would you like to to fix the bug" option.

Users can continue to set USERNAME to root (by setting USER in their Dockerfile or using other dev container features) if they want everything installed by the Node feature to be owned by root.

Copy link
Copy Markdown
Contributor

@abdurriq abdurriq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution! I've left a couple comments which I would appreciate if you could review and address.

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 this pull request may close these issues.

3 participants