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

Opt in #46

Open
eyelidlessness opened this issue Aug 8, 2021 · 5 comments
Open

Opt in #46

eyelidlessness opened this issue Aug 8, 2021 · 5 comments

Comments

@eyelidlessness
Copy link

Hi 👋 I happened on this just meandering around the Node GH. I think this would be an excellent thing to provide with Node, but I’m concerned about some of what’s proposed.

Background

Based on my reading of README/issues clarifying progress, the current behavior is:

  1. Install corepack
  2. Use Yarn/PNPM commands as if the package manager is already installed in $PATH
  3. If it’s not, corepath silently installs and executes it with the command entered

My understanding is that this will be be bundled with Node, eliminating step 1. While convenient, that makes the following steps troubling.

Risks

Users are executing code they didn’t take any affirmative steps to install. This is a clear security issue, but it’s (subtly) more too.

In terms of security risk, it’s similar to npx prior to NPM 7:

  • It obfuscates what action the user is actually performing
  • It executes network-delivered code without auditability
  • It puts the user at the mercy of security decisions made by others
  • Network delivered code can be poisoned in a variety of ways, including network API vulnerabilities, hijacked domains, malicious detection of curl … | …

The subtly more bit: false negatives. Users or orgs might have a policy of aliasing these package manager commands to harden or restrict them for their own usage. If an environment isn’t properly provisioned, this would undo those restrictions rather than failing as expected.

Solutions/prior art

NPM 7, thankfully, introduces a prompt when using npx to ask if the user wants to download and execute commands that aren’t already available on $PATH. This isn’t a panacea, but it’s a meaningful improvement over silently downloading and executing third party code.

The same technique could be used here. The downside is it’s a minor inconvenience, but the risks it would help to alleviate are huge.

Considerations

  • Automations aren’t interactive, and may expect to be able to bypass this
  • Meaningful thought should be put into mitigation of risk around that
@arcanis
Copy link
Contributor

arcanis commented Aug 8, 2021

In terms of security risk, it’s similar to npx prior to NPM 7:

  • It obfuscates what action the user is actually performing
  • It executes network-delivered code without auditability

While it's true that npx hides what you run (because it runs both local scripts and remote binaries) and doesn't allow ahead-of-time audits (because nothing prevents you from making a mistake and falling into a typosquatter's trap), none of those problems are relevant in Corepack's case.

The action being executed is always abundantly clear, because it matches what the user ran (ie it will always run a install with Yarn on "yarn install"), and the code can be audited ahead-of-time since the version is pinned within the package.json, and releases come from the official repositories for the supported package manager (and if we implement #10, it would even be enforced via signatures). If anything, the behavior should be more predictable than it currently is, not less.

It puts the user at the mercy of security decisions made by others

That's the case in any JS project, and is the entire point of packages: you store the behaviour of your application within an isolated and deterministic environment, shielded as much as possible from your global binaries. Interacting with these projects (whether it's by installing them, linting them, building them, or running them) then becomes a tacit aknowledgement of trust (which perhaps is a problem in itself, but one that is more related to the Node permission model than Corepack itself).

Also note that in a world where Corepack would be the default, you would still have the option of running npm i -g yarn to overwrite the Corepack binaries and enforce a single version.

Network delivered code can be poisoned in a variety of ways, including network API vulnerabilities, hijacked domains, malicious detection of curl … | …

I'm not sure I understand which network vulnerabilities you have in mind (unless you mean we should enforce https?) or what curl has to do with it, but domains can already be hijacked. In fact, Corepack actually makes this safer by giving us the option to implement release signing via multiple parties: if Node provided the public keys and each individual project the releases (#10), any attack would require to compromise the systems for both Node and the relevant package manager systems.

@eyelidlessness
Copy link
Author

While it's true that npx hides what you run (because it runs both local scripts and remote binaries) and doesn't allow ahead-of-time audits (because nothing prevents you from making a mistake and falling into a typosquatter's trap), none of those problems are relevant in Corepack's case.

The action being executed is always abundantly clear, because it matches what the user ran (ie it will always run a install with Yarn on "yarn install"), and the code can be audited ahead-of-time since the version is pinned within the package.json, and releases come from the official repositories for the supported package manager (and if we implement #10, it would even be enforced via signatures). If anything, the behavior should be more predictable than it currently is, not less.

I should have been more clear: the action I was referencing wasn't about the package manager's behavior, but executing the package manager itself, or whatever proxy script Corepack uses to bootstrap the package manager.

If this isn't opt-in, and it's built into Node, people running pnpm run foo won't know that what they're actually running is curl ... | bash && pnpm run foo.

It's likely low risk, but...

That's the case in any JS project, and is the entire point of packages: you store the behaviour of your application within an isolated and deterministic environment, shielded as much as possible from your global binaries. Interacting with these projects (whether it's by installing them, linting them, building them, or running them) then becomes a tacit aknowledgement of trust (which perhaps is a problem in itself, but one that is more related to the Node permission model than Corepack itself).

That's based on the assumption that the user installing/running dependencies knows that they are, allowing them to audit and shield how they see fit. Automatically pulling in another dependency, silently, which then installs those other dependencies, breaks that explicit trust both for the package manager itself and for all the packages it installs.

It also prevents control over how that installation occurs. Which is not something users normally worry about with package.json dependencies. I personally always avoid curl | bash, because it's vulnerable to server side detection and breaks auditability. And I've learned the hard way that PNPM's alternative recommendation—npm install -g pnpm—is not usable in my environment (it breaks Volta).

Also note that in a world where Corepack would be the default, you would still have the option of running npm i -g yarn to overwrite the Corepack binaries and enforce a single version.

Of course, but I would have to know to even do that. Which I would, if I try to run yarn and it's not in my $PATH, but not if it silently installs something.

I'm not sure I understand which network vulnerabilities you have in mind (unless you mean we should enforce https?) or what curl has to do with it, but domains can already be hijacked.

Network vulnerabilities (non-exhaustive):

  • Yes obviously enforce HTTPS
  • Using unsecured wifi (keep in mind the user might be just executing a project's scripts, not expecting to perform network access at all)
  • Anything that may be vulnerable in Corepack's network access itself

I mentioned curl because that's the default PNPM installation instruction. If you're not using that, probably great? And it seems obvious in hindsight you probably are not. But that raises the question to me:

Both curl | bash and npm install -g are well known and well understood in terms of risk, auditing, etc. Corepack's mechanisms less so. Will it be scrutinized (and more importantly professionally audited) similarly?

In fact, Corepack actually makes this safer by giving us the option to implement release signing via multiple parties: if Node provided the public keys and each individual project the releases (#10), any attack would require to compromise the systems for both Node and the relevant package manager systems.

Saved this for last, because I wanted to take the opportunity to make clarifications. But I do think #10 would go a long way towards easing my concerns about automatic usage in Node. But I'd still like to be given the choice, and I don't think there's a good justification not to.

Again with npx as an analogy, I consider it less risky and more acceptable to use because it now informs me that it intends to install something from a remote source. If simply having Node installed on my system introduced automatic, silent execution of network-installed code I would start looking at alternatives to Node.

@jirutka
Copy link

jirutka commented Feb 8, 2022

I totally agree with @eyelidlessness and it really annoys me that you pushed corepack even into previous LTS version! I maintain the nodejs package in Alpine Linux and right now I’m wondering what to do about it, whether to remove corepack completely or just patch it so that at least it never writes to /usr (next to node binary)…

@arcanis
Copy link
Contributor

arcanis commented Feb 8, 2022

patch it so that at least it never writes to /usr (next to node binary)

Is there another place where the binaries would be available in the users' PATH (at least the current one) without requiring bashrc schenanigans?

@Kurt-von-Laven
Copy link

@jirutka, I suspect you are already aware of this, but for the sake of others who happen upon this thread, even Node 18.0.0 ships with Corepack disabled by default.

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

No branches or pull requests

4 participants