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

Mitigate dependency confusion #348

Merged
merged 1 commit into from
Feb 16, 2021
Merged

Mitigate dependency confusion #348

merged 1 commit into from
Feb 16, 2021

Conversation

GunnarFarneback
Copy link
Contributor

@GunnarFarneback GunnarFarneback commented Feb 13, 2021

This PR adds a dependency confusion mitigation mechanism for public registries beside General, which are particularly exposed to the vulnerability since their UUIDs are, well, public.

The idea, once this PR goes live, is that public registries who want to take part of this mitigation would submit a PR against https://github.com/JuliaRegistries/General/blob/master/.github/workflows/automerge.yml to add their repo URL to the RegistryCI.AutoMerge.run argument public_registries. When automerge runs, it will clone those repositories and check for a UUID conflict when examining a new-package request. If a conflict is found automerge is blocked, usually.

There is one exception to this rule. Assume you want to add a package to General that has previously lived in one of the checked public repositories. Obviously this will trigger a UUID conflict. However, in this case the package repository will match, which means that

  1. An attacker has no way to add rouge versions.
  2. Registrator wouldn't have accepted the registration request from someone unauthorized.

So the rule is that automerge is blocked for conflicting UUIDs, unless repository URL and package name match. (Same UUID with different names is no good, regardless of dependency confusion.)

There are some outstanding questions of how much time this takes and whether it gracefully handles connection problems for the public registries.

Cc: @timholy

@oxinabox
Copy link
Contributor

A slight generalization would be if we could hook this up either to a registry repo URL,
or just a URL that contains a plain text file with one UUID per line.
That way private registries would have the option of publishing their list of UUIDs, and we could protect them.

@GunnarFarneback
Copy link
Contributor Author

I agree that some blacklisting solution also for private registries would be desirable but I'm not convinced that it should be plain text and for this PR the focus is to give the more exposed public registries a tool to work with. However, a private registry which really wants to take part in this scheme could make a public dummy registry, which wouldn't have to be much more than a list of UUIDs.

@GunnarFarneback
Copy link
Contributor Author

For integration testing it should work to set up a "public registry" in a temporary directory and point to it with a file:// URL.

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 14, 2021
@bors
Copy link
Contributor

bors bot commented Feb 14, 2021

try

Build failed:

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 14, 2021
@bors
Copy link
Contributor

bors bot commented Feb 14, 2021

try

Build failed:

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 14, 2021
@bors
Copy link
Contributor

bors bot commented Feb 14, 2021

try

Build failed:

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 14, 2021
@bors
Copy link
Contributor

bors bot commented Feb 14, 2021

try

Build failed:

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 14, 2021
@bors
Copy link
Contributor

bors bot commented Feb 14, 2021

try

Build failed:

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 14, 2021
@bors
Copy link
Contributor

bors bot commented Feb 14, 2021

try

Build failed:

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 14, 2021
@bors
Copy link
Contributor

bors bot commented Feb 14, 2021

@GunnarFarneback GunnarFarneback changed the title [WIP] Mitigate dependency confusion Mitigate dependency confusion Feb 14, 2021
# registries. Preferably they should also be cloned only once and then
# just updated to mitigate the effect of them being temporarily
# offline. This could be implemented with the help of the Scratch
# package, but requires Julia >= 1.5.
Copy link
Member

Choose a reason for hiding this comment

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

I'd be in favor of bumping the minimum version to Julia 1.5 if it helps here; the syntax benefits would be nice too (no more x=x in keyword arguments). And since RegistryCI is usually run by itself in CI (and is a leaf of the dependency tree), one doesn't need to upgrade their whole stack to use the new version.

Copy link
Member

Choose a reason for hiding this comment

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

a related concern is if an external registry gets corrupted and has an invalid TOML file, say. Then RegistryCI would throw everytime it gets to this check. I wonder if we should use TOML.tryparsefile and fail the check if we get an error, so at least RegistryCI doesn't crash in that event, but we just fail this check each time. That would still be pretty disruptive though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can definitely be done with Scratch, but maybe that's a secondary concern.

Invalid TOML files or other registry irregularities is a real risk. The easiest protection would be a try/catch, but that's rather heavyhanded.

Copy link
Member

@ericphanson ericphanson Feb 15, 2021

Choose a reason for hiding this comment

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

I think a try/catch would be OK; it seems like that code should only fail if there are issues cloning and parsing the registry. And if we fail the check in the event of an error, we won't be sweeping any issues under the rug, since automerge will fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's slightly unfair to the affected PRs since it's in no way their fault (well, unless they have made a DOS attack against the registry to try to disable the check...), but I agree that getting attention to the issue is of higher value.

Does someone know how clone_repo behaves when there are network problems?

Copy link
Member

@ericphanson ericphanson Feb 15, 2021

Choose a reason for hiding this comment

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

yeah, agreed it's not great for the affected PRs... it seems too risky to pass the check if there's an error though, since then an attacker could even do something like add a new public registry they control, then invalidate the TOML or cause network issues in that registry, and then get their package in during the auto-pass window before the problem is fixed.

no idea about what happens during network issues, sorry

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a try/catch with an explanatory message.

src/AutoMerge/dependency_confusion.jl Outdated Show resolved Hide resolved
src/AutoMerge/dependency_confusion.jl Outdated Show resolved Hide resolved
src/AutoMerge/dependency_confusion.jl Outdated Show resolved Hide resolved
src/AutoMerge/dependency_confusion.jl Outdated Show resolved Hide resolved
@StefanKarpinski
Copy link

It seems like we should do this as well as some other mitigations — defense in depth and all that. I agree that if we have a mechanism for private repos to publish this that it should be salted and hashed. That can be done separately. We can develop that in the future. Good to let public non-General registries have some protection ASAP and being able to register themselves here is simple and effective.

@DilumAluthge
Copy link
Member

Eric and Gunnar, once you're satisfied with this, go ahead and merge it.

@DilumAluthge
Copy link
Member

And then register a new release of RegistryCI so that we can deploy this.

@GunnarFarneback
Copy link
Contributor Author

GunnarFarneback commented Feb 15, 2021

Before merging it would be good to squash it, there's lots of nonsensical history from the bors iterations. Done

@GunnarFarneback
Copy link
Contributor Author

bors try

bors bot added a commit that referenced this pull request Feb 15, 2021
@bors
Copy link
Contributor

bors bot commented Feb 15, 2021

Copy link
Member

@ericphanson ericphanson left a comment

Choose a reason for hiding this comment

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

LGTM, let’s merge tomorrow so we’re awake to handle any issues that arise

@GunnarFarneback
Copy link
Contributor Author

bors merge

@bors
Copy link
Contributor

bors bot commented Feb 16, 2021

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.

5 participants