Live data from Hacker News

How Go mitigates supply chain attacks

go.dev

191–200 of 265 posts

Re: How Go mitigates supply chain attacks

#191

Earlier quoted context omitted.

> You already know[0] the answer to the most of that question, so I don't know why you're asking that part again. My point is that once you start editing (either by hand or through a tool) the version numbers in the second section, then that's no longer cached state derived entirely from the go.mod files of your dependencies which can be regenerated from scratch. It contains some human-authored decisions and regenera…

> It contains some human-authored decisions and regenerating that section without care will drop information on the floor. > Maybe the answer here is that even when you ask the tool to remove unneeded transitive dependencies, it won't roll back to an earlier version of bar? So it will keep it at 1.2? I'm not aware of any package management system that will remember dependencies you no longer depend on, except by huma…

> I've done my best to explain it to you, literally multiple hours of my day writing these comments.

Not the op, but wanted to express appreciation here. I read all your comments and feel like I gained a lot of clarity and insight from them. Would love to read your blog if you had one.

Re: How Go mitigates supply chain attacks

#192
post #131

Earlier quoted context omitted.

How does go deal with the diamond dependency issue when transitively you've a dep on the same package via two different paths?

As far as I understand it... the import path that you use to import a package acts as its identity, and only one version of any given package will be installed. The way that it will determine this is by choosing the lowest version specified in any package that depends on a given package. Major versions of packages are required to have different import paths with Go modules, so when depending on two different major ve…

I think you might have a small typo here:

> by choosing the lowest version specified in any package that depends on a given package

It picks the highest version specified in any of the requirements. (That's the minimal version that simultaneously satisfies each individual requirement, where each individual requirement is saying "I require vX.Y.Z or higher". So if A requires Foo v1.2.3 and B requires Foo v1.2.4, v1.2.4 is selected as the minimal version that satisfies both A and B, and that's true even if v1.2.5 exists).

Re: How Go mitigates supply chain attacks

#193
post #131

Earlier quoted context omitted.

As far as I understand it... the import path that you use to import a package acts as its identity, and only one version of any given package will be installed. The way that it will determine this is by choosing the lowest version specified in any package that depends on a given package. Major versions of packages are required to have different import paths with Go modules, so when depending on two different major ve…

Okay but if I depend on A and A depends on C and has it pinned at 1.5, but I also depend on B and B has C pinned on 1.8, then I get 1.5? But what happens if C is on 1.8 because it doesn't work with 1.5 because an API it needs doesn't exist in 1.5? Are we not talking about the transitively pinned dependencies in the "lock" section, or are we talking about logical constraints? Logical constraints would make more sense,…

> Okay but if I depend on A and A depends on C and has it pinned at 1.5, but I also depend on B and B has C pinned on 1.8, then I get 1.5?

No, you end up with the highest explicitly required version. So 1.8 in that scenario, if I followed. (Requiring 1.5 is declaring support for "1.5 or higher". Requiring 1.8 is declaring support for "1.8 or higher". 1.8 satisfies both of those requirements).

> if the constraints on C across various transitive deps are > 1.5 and > 1.8, and 1.9 or 1.10 exists, I probably want the last version of those.

By default, you get 1.8 (for reasons outlined upthread and in the blog post & related links), but you have the option of getting the latest version of C at any time of your choosing (e.g., 'go get C@latest', or 'go get -u ./...' to get latest versions of all dependencies, and so on).

Also, you are using the word "pin". The way it works is that the top-level module in a build has the option to force a particular version of any direct or indirect dependency, but intermediate modules in a build cannot. So as the author of the top-level module, you could force a version of C if you needed to, but for example your dependency B cannot "pin" C in your build.

Re: How Go mitigates supply chain attacks

#194
>> The only commands that will change the go.mod (and therefore the build) are go get and go mod tidy. These commands are not expected to be run automatically or in CI, so changes to dependency trees must be made deliberately and have the opportunity to go through code review.

GO doesn't do jack shit to mitigate supply chain attacks. Version pinning with checksum and that is it. But what could go do? Solve supply chain attacks as a language feature? That doesn't even make sense.

Application developers using Go must prevent supply chain attacks against their applications. So go get some SAST for your pipeline.

Sure there is truth in saying: always verify your dependencies (and their dependencies) yourself with a code review on every update. But you should not do that alone, so let's talk about collaborative vulnerability management. (there is more to sast than vulnerability assessment, but we have to start somewhere)

Let's say repositories that publishe go modules should also publish a curated list of known vulnerabilities (including known supply chain attacks) for the modules they publish. This curation is work: reports must be verified before being included in the list and they must be verified quickly. This work scales with the number of packages published. And worse, modules could be published in more than one repository, module publishing repositories can be different from the modules source code repository, and vulnerability lists can exist independent from these repositories - so reports should be synced between different list providers. Different implementations and lack of common standards make this a hard problem. And implicit trust for bulk imports could open the door for takedown attacks.

There is an argument that vulnerability listing should be split from source and module publishing: each focusing on their core responsibility. For supply chain attacks this split in responsibilities also makes it harder for an attacker to both attack suppliers and suppress reports. But for all other issues it increase distance as reports must travel upstream. And it creates perverse incentives, like trying to keep reports exclusive to paying customers.

To pile on the insanity: reports can be wrong. And there are unfixed CVEs that are many years old (well ok maybe not for go... yet). Downstream there are "mitigated" and "wont-fix" classifications for reports about dependencies and many SAST tooling can't parse that for transitive dependencies.

Really, supply chain attacks are the easy case in vulnerability management, because they are so obviously a "must-fix" when detected. (and to please the never update crowd: for a downstream project "fix" can mean not updating a dependency into an attacked version. you are welcome)

Long story short: go get some SAST in your pipelines to defend against supply chain attacks. Don't pretend pinning the version and half-assing a code review when you update them is actually solving supply chain attacks. Don't tell me everyone who uses go can find a sophisticated data bomb or intentional rce in some transitive dependency of some lib they update to a new feature release. And don't give me some "well if its transitive then the lib dev should have." Should have doesn't solve shit.

Examples for Vulnerability Assessment in GO dependency graphs are GitLabs Gemnasium ( https://gitlab.com/gitlab-org/security-products/gemnasium-db... ) or GitHubs Dependabot ( https://github.com/advisories?query=type%3Areviewed+ecosyste... ) among many, many others. Not recommendations, just examples!

Tools like these help you sort out supply chain attacks that other people have already found, before you update into them and push them downstream. Collaboration is useful. Sure you are still left with reading the source changes of all dependency update, because who knows, you may be the first one to spot one, but hey, good for you.

Re: How Go mitigates supply chain attacks

#195

Earlier quoted context omitted.

If I'm understanding you properly, recursive lockfiles means that if I depend on some chain of dependencies A->B->C->D->E, and E has a security vulnerability that they patch in a new version, I have to wait for A B C D and E to all update their lockfiles before the security vulnerability will be patched on my system?

That's not correct. You can unilaterally decide to update the version of E without waiting for anyone. Alternatively, if only C for example decides to update their required version of E, you would get that version of E if you updated your version of C (directly or indirectly), without needing to directly do anything with E yourself. Slightly longer explanation of the mechanics here: https://news.ycombinator.com/item?…

But wouldn't that have the same issue then? Developers decide to update their dependencies to patch any security vulnerabilities, and wind up adding installing node-ipc's malicious update

Re: How Go mitigates supply chain attacks

#196

Go is a distillation of many decades of software engineering experience. The people behind Go (e.g., Russ Cox) have learned from history. The peanut gallery loves to complain about superficial aspects of Go. Typically these are people with little or no actual experience using the language and tools. They fixate on imagined problems that don't matter in practice. But anyone who has used Go full-time for a few years is…

A few years of Golang under my belt and I still hate it. Russ Cox and co. seem incredibly arrogant to me in that they can ignore decades of PL research only to reinvent a bizarre way of achieving what other languages do in a more standard way (package management, error handling), or just adopt that standard super late (generics). Go has some great qualities and you can make great software with it no doubt. But I find…

What is your favorite PL?

Re: How Go mitigates supply chain attacks

#197
post #90

Earlier quoted context omitted.

> any new, poisoned version of libB Conversely, you will get any old security-buggy version of libB instead. Most package managers when adding a new dependency assume newer versions are "better" than older versions. Go's minimum version system assumes older is better than newer. I don't think there's any clear argument you can make on first principles for which of those is actually the case. You'd probably have to do…

Every change that fixes a security issue implies the existence of a change that introduced the security issue in the first place. Why is bumping a version more likely to remove security issues instead of introduce them? The reason why older is better than newer has more to do with the fact that the author has actually tested their software with that specific version, and so there's more of a chance that it actually w…

Security issues aren't introduced intentionally, oftentimes they are found much later on in code that was assumed to be secure. Like the SSL heartbleed vulnerability. Once a vulnerability like that is discovered, you _want_ every developer to update their deps to the most secure version

Re: How Go mitigates supply chain attacks

#198

Earlier quoted context omitted.

This really depends on the specific package manager: if you're building an application in Rust, its lockfile will contain the full tree of dependencies, locked to a specific version.

This is also true of npm and yarn, as far as I can tell: package-lock.json and yarn.lock contain the exact version of every transitive dependency.

I always forget the exact semantics, but the parent's description of them as "recursive" is not the same as Cargo; Cargo determines the full tree and writes out its own lockfile, if dependencies happen to have a Cargo.lock inside the package, it's ignored, not used.

Re: How Go mitigates supply chain attacks

#199
post #70

Earlier quoted context omitted.

It’s very subtle, but there are some important differences. For example, lockfiles are not recursive in NPM: the NPM package (usually?) does not contain the lockfile and does not adhere to it when installed as a dependency. It will pick the newest version of dependencies that matches the spec in package.json. Go mod files are used recursively, and rather than try to pick the newest possible version, it will go with t…

This really depends on the specific package manager: if you're building an application in Rust, its lockfile will contain the full tree of dependencies, locked to a specific version.

I might be misunderstanding GP, but I think what they're saying is that when package A depends on package B, building package A will use B's lockfile. Assuming that's the case, I think this is generally not how Rust does things, as by default Cargo.lock is explicitly listed in the .gitignore when making a library crate, although there's nothing stopping anyone from just removing that line. I think I remember reading in documentation somewhere that checking in Cargo.lock for libraries is discouraged (hence the policy), but I don't recall exactly where since it's been so long. (That being said, there's a pretty decent chance you were the one who wrote that documentation, so maybe you might remember!)

Re: How Go mitigates supply chain attacks

#200
post #163

Earlier quoted context omitted.

I'm sorry but I'm not super familiar with the workflow for working with dependencies in Go, I've only read about it. You say: > Raise the version there. Am I to understand that it's common to hand-edit the version constraint on a transitive dependency in your go.mod file? But that transitive dependency was first added there by the Go tool itself, right? How does a user easily keep track of what bits of data in the go…

> Am I to understand that it's common to hand-edit the version constraint on a transitive dependency in your go.mod file? No, run `go get @ ` and Go will do it for you.

>No, run `go get @` and Go will do it for you.

The point GP was making is that it's not a given you'll know that there's a security vuln in a sub-sub-sub-dependency of your app. Is it reasonable to expect developers to manually keep tabs on what could be dozens of libraries that may or may not intersect with the dependencies of any other apps you have on the go?

Maybe for Google scale where you can "just throw more engineers at the problem".

Post reply on HN