Live data from Hacker News

How Go mitigates supply chain attacks

go.dev

161–170 of 265 posts

Re: How Go mitigates supply chain attacks

#161
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,…

I'm fairly sure Go Modules does not support what you’re describing. It specifically avoided having a SAT solver (or something similar), unlike most package managers. You specify a minimum version, and that’s it. 1.8 would be selected because it is the highest minimum version out of the options 1.5 and 1.8 that the dependencies require. Unless you edit your go.mod file to require an even higher version, which is an option. Alternatively, you can always replace that transitive dependency with your own fork that fixes the problems, using a “replace” directive in your go.mod file.

If your dependencies are as broken as you’re describing, you’re in for a world of hurt no matter the solution. I also can't remember ever encountering that situation.

Re: How Go mitigates supply chain attacks

#163

Earlier quoted context omitted.

The version constraint is always listed in your top level go.mod file, so you know the dependency exists, no digging into the dependency tree required at all, and it’s not hidden in some lock file no one ever looks at. Plus, there are plenty of tools that help you with this problem, including the language server helping you directly in your editor and Dependabot on GitHub. I’m not aware of any languages that send you…

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.

Re: How Go mitigates supply chain attacks

#164

Earlier quoted context omitted.

Far easier to work with and understand when you dont need to perform a massive disruptive ceremony to handle exceptions. I've been working full time in java for the past 4 years and basically no one handles exceptions because its so cumbersome and bad to read. Not so in Go. Go does it better.

> Far easier to work with and understand when you dont need to perform a massive disruptive ceremony to handle exceptions. I've been working full time in java for the past 4 years and basically no one handles exceptions because its so cumbersome and bad to read. Not so in Go. Go does it better. No Go doesn't do it better, Go just doesn't give you the choice, from a convention perspective. You have a discipline issue…

> You have a discipline issue with Java, it is not an issue in the language itself, it's with you and your team.

Only if you consider the "language" to be only the language itself, i.e. syntax + semantics. When people refer to a "programming language", they almost always mean the whole package, i.e. the language itself, the community, package ecosystem, tooling, conventions, etc.

If there is widespread usage of an incorrect pattern, then it is the failure of the language for not addressing it. For example, C's lack of package/build management.

The experiences with exception-based languages others in this thread brought up mirror my own. No thinking about error handling whatsoever, empty `catch` blocks, dumping stacktraces in lieu of error messages, etc.

Needing discipline to prevent those is exactly the problem. The language is what should guide you towards a better way, in this case explicitly returning errors.

Re: How Go mitigates supply chain attacks

#165

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? "Common" is probably not accurate, but what's wrong with hand editing it? If you mess up, your project won't compile until you fix it. You can update the versions a dozen different ways: editing by hand, `go get -u ` to update a specific dependency, `go get -u ./...` to update all dependencies, us…

> 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 regenerating that section without care will drop information on the floor.

Imagine you:

1. Add a dependency on foo, which depends on bar 1.1.

2. Decide to update the version of bar to 1.2.

3. Commit.

4. Weeks later, remove the dependency on foo and tweak some other dependencies. Tidy your go.mod file.

5. Change your mind and re-add the dependency on foo.

At this point, if you look at the diff of your go.mod file, you see that the indirect dependency on bar has changed from 1.2 to 1.1. Is that because:

A. You made a mistake and accidentally lost a deliberate upgrade to that version and you should revert that line.

B. It's a correct downgrade because your other dependency changes which have a shared dependency on bar no longer need 1.2 and it is correctly giving you the minimum version 1.1.

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?

With other package management tools, this is obvious: Any hand-authored intent to pin versions—including for transitive dependencies—lives in one file, and all the state derived from that is in another.

> In my experience, most people either use Dependabot to keep up to date with their dependencies, or they update the dependencies using VS Code to view the go.mod file and click the "buttons"(/links/whatever) that the language server visually adds to the file to let you do the common tasks with a single click. They're both extremely simple to use and help you to update your direct and indirect dependencies.

This sounds like you more or less get the same results as you would in other package managers, but with extra steps.

I don't know. I guess I just don't understand the system well enough.

Re: How Go mitigates supply chain attacks

#166
post #148

Earlier quoted context omitted.

That seems reasonable. I think I personally lean towards keeping them in separate files entirely because I like a clearer separation between human-authored content and machine-derived state.

but if you ever bump up the version of an indirect dependency (maybe to pick up a bugfix earlier), is this now a direct or indirect dependency?

In other systems, you do that by creating a direct dependency. Even though your code doesn't directly import the module, you author an explicit dependency because you care about the version of the module that your app ends up using.

This way, there's a clear separation between human-authored intentional dependencies and the automatically-derived solved versions and transitive dependencies based on that.

If you see a diff in your package manifest, you know a human did that. If you see a diff in the lockfile, you know that was derived from your manifest and the manifests you depend on. The only human input is when they choose to regenerate the lockfile and which packages they request to be unlocked and upgraded. That's still important data because regenerating the lockfile at different points in time will produce different results, but it's a different kind of data and I think it's helpful to keep it separated.

Re: How Go mitigates supply chain attacks

#167
post #106

Earlier quoted context omitted.

Perhaps "A module may have a text file named go.sum in its root directory, alongside its go.mod file. The go.sum file contains cryptographic hashes of the module’s direct and indirect dependencies." And "If the go.sum file is not present, or if it doesn’t contain a hash for the downloaded file, the go command may verify the hash using the checksum database, a global source of hashes for publicly available modules." S…

> the go command may verify the hash If we're talking about reproducible builds, the word "may" seems concerning here?

I suspect the primary purpose of the word "may" in that sentence is that you can choose to disable checking the hash against the Certificate Transparency style https://sum.golang.org. In other words, you can opt out. If you do, you fall back to your local go.sum file, which is more-or-less a "TOFU" security model: https://en.wikipedia.org/wiki/Trust_on_first_use

More on sum.golang.org: https://go.googlesource.com/proposal/+/master/design/25530-s...

Re: How Go mitigates supply chain attacks

#168

Earlier quoted context omitted.

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,…

I'm fairly sure Go Modules does not support what you’re describing. It specifically avoided having a SAT solver (or something similar), unlike most package managers. You specify a minimum version, and that’s it. 1.8 would be selected because it is the highest minimum version out of the options 1.5 and 1.8 that the dependencies require. Unless you edit your go.mod file to require an even higher version, which is an op…

Well after 12 years of using ruby and bundler and maintaining a bundler-ish depsolver at work, and playing around a bit with cargo, I can say that it is becoming clearer as to why I don't grok go modules at all.

The lack of a depsolver is a curious choice...

I don't think my example was remotely "broken" at all, that's just another day doing software development.

Re: How Go mitigates supply chain attacks

#169

Earlier quoted context omitted.

> Am I to understand that it's common to hand-edit the version constraint on a transitive dependency in your go.mod file? "Common" is probably not accurate, but what's wrong with hand editing it? If you mess up, your project won't compile until you fix it. You can update the versions a dozen different ways: editing by hand, `go get -u ` to update a specific dependency, `go get -u ./...` to update all dependencies, us…

> 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 human error when you forget to remove that dependency but keep punishing yourself and others by making them build and install that unused dependency. No matter where the information lived before it was deleted, it's still up to the human to do the right thing in a convoluted scenario like you're describing.

> With other package management tools, this is obvious: Any hand-authored intent to pin versions—including for transitive dependencies—lives in one file, and all the state derived from that is in another.

That doesn't solve anything if you don't look go back to the commit that had that information. If you do go back to that commit, you have all the information you need right there anyways. You can add your own comments to the `go.mod` file, so if something changed for an important reason, you can keep track of that (and the why) just as easily as you can in any other format. Actually, easier than some... does package.json even support comments yet? But it only matters if you go back and look at the previously-deleted dependency information instead of just blindly re-adding it as I imagine most people would do, which is a huge assumption.

>> It seems like humans often want to assume that "things probably suck just as much everywhere else, just in different ways, and those people must simply be hiding it", but that's not always the case.

> This sounds like you more or less get the same results as you would in other package managers, but with extra steps.

> I don't know. I guess I just don't understand the system well enough.

I've done my best to explain it to you, literally multiple hours of my day writing these comments. Maybe I suck at explaining, or maybe you're not interested in what I have to say, or maybe I'm somehow completely wrong on everything (but no one has bothered to explain how). "More or less the same" is not the same. The nuances make a huge difference, and Go Modules has done things incredibly well on the whole. Package managers aren't a zero sum game where you shuffle a deck of pros and cons, and you end up with the same number of pros and cons at the end.

Re: How Go mitigates supply chain attacks

#170

Earlier quoted context omitted.

> the go command may verify the hash If we're talking about reproducible builds, the word "may" seems concerning here?

I suspect the primary purpose of the word "may" in that sentence is that you can choose to disable checking the hash against the Certificate Transparency style https://sum.golang.org . In other words, you can opt out. If you do, you fall back to your local go.sum file, which is more-or-less a "TOFU" security model: https://en.wikipedia.org/wiki/Trust_on_first_use More on sum.golang.org: https://go.googlesource.com/pr…

Thank you for the clarification!
Post reply on HN