Live data from Hacker News

How Go mitigates supply chain attacks

go.dev

241–250 of 265 posts

Re: How Go mitigates supply chain attacks

#241
post #200
post #163

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

> 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?

Well, in the NPM model you need at least one transitive dependency to notice it and upgrade, and you need to notice your transitive dependency upgraded. But also, it might upgrade despite nobody asking for it just because you set up a new dependency.

In the Go model... you need at least one transitive dependency to notice it and upgrade, and you need to notice your transitive dependency upgraded. But at least it won't ever upgrade unless someone asked for it.

Re: How Go mitigates supply chain attacks

#242

Earlier quoted context omitted.

As far as I know that doesn't solve the problem for transitive dependencies, which can still be resolved to different versions without a lock file.

No it’s the same behaviour for transitive dependencies, if two libraries require different versions of the same transitive dependency, the newer one is chosen. Deterministic no lock file required.

> if two libraries require different versions of the same transitive dependency, the newer one is chosen.

Unfortunately, this is not how maven works. It picks the version required by the dependency nearest in depth to the project root, breaking ties by first listed in the file. It is deterministic, but it's not what anyone ever wants by default.

(This is called "dependency mediation" if you want to Google it.)

Re: How Go mitigates supply chain attacks

#243

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…

Who is doing "PL research" in package management?

Re: How Go mitigates supply chain attacks

#244
post #202

Earlier quoted context omitted.

Because errors are just return values in golang, and it's not possible to write a linter to guarantee that they're handled, there's always going to be the possibility of them being overwritten by accident. Not to mention due to the lack of composability, you end up with even more hacks like what gorm does, making it even easier to mishandle errors. > Go does not have a concept of errors Exactly the problem. There nee…

> Because errors are just return values in golang, and it's not possible to write a linter to guarantee that they're handled That's true of all types, though. Nothing specific to errors applies there. Spend enough time in Go and that's going to bite you, even when the error interface is nowhere to be found. You've still not made clear what's special about errors that makes the concern about errors, not all types, mor…

> You've still not made clear what's special about errors that makes the concern about errors, not all types, more than theoretical.

The way errors are handled in golang, its possible to ignore them accidentally. This doesn't happen with other types because you don't keep constantly overwriting the same variable over and over (immutability is generally good, another thing that golang lacks), increasing the likelihood of ignoring an error.

E.g.

    a, err := foo()
    if err != nil { ... }
    b, err := bar(a)
    c, err := baz(b)
A linter will complain if b or c are unnused, but it cannot complain that err is unnused, because it is used and has been declared before. Whats worse something like this

   a, err := foo()
   if err != nil { return err }
   b, errBar := bar(a)
   if errBar != nil { return err } // oops
> The computer doesn't have a concept of errors either.

This is exactly the same reasoning that golang authors gave about why it doesn't have a notion of optional or non nullable pointers. To the computer, a pointer is a pointer. This isn't the way to think if we want to make reliable and readable programs. Computers don't have notions of methods or inheritance or even functions either, everything is a jmp of some sort.

The entire field of programming is to make it easier for humans to reason about code, OOP, functional programming, etc. Otherwise, we'd all be writing assembly or machine code.

Errors are special because they need to carry certain state about the program when an error happened (e.g. stack trace). golang errors are basically strings, which is why people invented frameworks to capture stack traces in golang errors. On the code bases I worked on, this building of the call stack is either done manually (by wrapping errors) or by concatenating strings, or by logging errors everywhere and capturing the stack at the log site. Quite horrible experience overall and just keeps polluting the code with boilerplate the makes it even less clear what's going on.

I've experienced the issues that come out of golang's overly simplistic design. The overly verbose code that is hard to see what its doing at first glance, the non-composability of errors, the mishandling of errors, etc.

Other than exceptions, languages like Rust have it much better than golang. You still get to have explicit error handling, but with much superior ergonomics that make them much more difficult to mishandle.

Re: How Go mitigates supply chain attacks

#245
post #186

Earlier quoted context omitted.

Only with checked exceptions, which I think most of java has made an anti-pattern now?

No, it's got nothing to do with checked exceptions. It's from not wanting all the error-handling noise between the two steps of your happy-path logic. And at least in extant Java code and many Java programmers I interview, it's not considered an anti-pattern (but I agree it should be).

It's quite trivial to write a generic function that handles a particular exception else returns a default value, which addresses the above scenario.

    final var x = getOrDefault(C::foo, Something.class, quux());
    return x.bar();
And now with pattern matching in Java, it's trivial to write something similar to Rust's/Scala's `Result`/`Try` types and be explicit about all exceptions.

I haven't compiled the following, but it's along the lines of:

    final var x = switch (foo()) {
        case Ok(var r) -> r;
        case Error e -> quux();
    }
    return x.bar();

Re: How Go mitigates supply chain attacks

#246

I'm annoyed by the false dichotomy that colors most discussions around package management that there are only two solutions to publishing software packages: 1. a carefully curated professionally maintained standard library, 2. the complete wild west where anything goes. It's not really "false" because this is the reality of how package managers are designed today, but it's false in the sense that it doesn't have to b…

The original solution is the intermediate solution. You can build an entire useful userspace around nothing but libc and an ssl lib (take your pick between OpenSSL or GnuTLS usually). That is effectively what busybox is. Need to do some heavyweight math in Fortran? BLAS and LAPACK probably have everything you need. You can get really far with a C++ application using nothing but Boost. For whatever reason, newer langu…

I don't think your suggestion works for Web Development.

Imagine an application that talks to a Postgres DB, a Redis cache, several AWS services (e.g. S3, Lex) and also has to be able to parse excel documents. I've worked on applications like that. That's a whole lot of libraries you'll need to include. But I don't think you should include all of them in some "standard library", most people are not gonna need most of these dependencies.

I agree that left-pad is ridiculous (over in the JVM space, there is apache-commons, which also provides e.g. left-pad but of course it's not the only functionality that it provides), but using only 2-3 libraries isn't realistic either.

Re: How Go mitigates supply chain attacks

#247

Earlier quoted context omitted.

It is trivial to manually upgrade dependencies in NPM. You just use `npm update ` with an optional version number if you want. And upgrading all dependencies of a Go package is also a single command. So honestly it seems like there is very little difference. My main point here is the trade-off. Either you reduce the friction for upgrades, and run the risk of malicious upgrades like node-ipc. Or you increase the frict…

If I set up a new Node project I get the highest 'supported' version of whatever. If I add a new dependency I get the latest version of any transitive dependency I didn't already have. As far as I know that's impossible to disable. That's the automated upgrade I mean.

I see, in that case yes Go does have more tooling for being able to install the minimum vs the latest of all packages, using their `update` command if you want the latest. But it would also be trivial for Node to add a command to grab the minimum of all dependencies when installing new packages. They just haven't felt the need to add such a feature. Because again, it comes down to which side you want to encourage: installing minimum versions to prevent malicious updates, or installing latest to patch security vulnerabilities.

Re: How Go mitigates supply chain attacks

#248
post #220

Earlier quoted context omitted.

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

My statement had nothing to do with intent. Conversely, once a vulnerability is introduced (intentionally or not), you don't want every developer to update their deps to the newly insecure version.

Exactly, so it's a trade-off, do you want to encourage updates at the risk of malicious updates (like with node-ipc). Or do you want to add friction to updates and thus risk security vulnerabilities persisting for longer. Node chooses one approach, Go chooses the other.

Re: How Go mitigates supply chain attacks

#249
post #155

Earlier quoted context omitted.

> An uncaught managed exception will not compile, and an uncaught unmanaged exception might terminate your program. While it's nice that uncaught exceptions stop the program from continuing with invalid state, it'd be strictly better to not have them in the first place. That's what languages with explicit error returns provide - can't have uncaught errors if you're forced to explicitly deal with every possible error.…

> This is a deliberate decision to ignore the error - the programmer needed to explicitly type this out. As such, it's not a problem at all. Even if it was, writing a linter to detect it would be trivial. So is not handling Java exceptions is deliberate, and again, Java forces you to handle managed exceptions. > While it's nice that uncaught exceptions stop the program from continuing with invalid state, it'd be stri…

> So is not handling Java exceptions is deliberate

That's the thing, there's no visible difference between a deliberately ignored exception and a purposefully ignored one. The code looks exactly the same:

    foo()
Did I purposefully ignore the exception or not?

Can `foo()` even throw an exception?

There's no way to know.

Once you've programmed with explicit error returns, coming back to exceptions is unnerving. Anything may or may not throw at any time and there's no way to know.

> Java forces you to handle managed exceptions.

I'm referring to the far more common unchecked exceptions. Java's checked exceptions are a whole another bag of demons I don't want to get into right now.

> Errors as value in Go are purely a convention, nothing precludes you from doing the exact same thing in Java, or JavaScript or any other possible language that has exceptions.

Thing is, conventions matter. A lot. The proof is in the pudding:

- what's the ratio of exception-based Java code to error-based Java code?

- what's the ratio of error-based Go code to exception-based Go code?

Somehow both ratios end up close to 100%, despite neither language forcing their users to use a specific error handling method.

That's because there's space between forcing something and completely ignoring it. I often call it "nudging". Languages often do, and should, nudge their programmers into certain ways of programming that fit their philosophy.

> Furthermore, Go has (stupid) exceptions, it's called panics.

I assume by "stupid" you mean that panics are underfeatured compared to "real" exceptions. That's intended, you're not supposed to catch panics in the first place. They're only there to address runtime issues that are almost always unrecoverable: running out of memory, out-of-bounds array access, regex compilation failure, etc.

Re: How Go mitigates supply chain attacks

#250

I'm annoyed by the false dichotomy that colors most discussions around package management that there are only two solutions to publishing software packages: 1. a carefully curated professionally maintained standard library, 2. the complete wild west where anything goes. It's not really "false" because this is the reality of how package managers are designed today, but it's false in the sense that it doesn't have to b…

This is kind of interesting. In the Linux world, you have package maintainers who (among other things) vet and vouch for the quality of the packages they maintain. I think there are similar things in the Docker ecosystem these days (since Docker really did/does seem to be the wild west). It could be interesting if there was a similar concept for Go (and/or other ecosystems), except that instead of actually packaging…

Seems like this would be equivalent to running your own module proxy / sumdb with a whitelist, which e.g. Athens can do.
Post reply on HN