> There is no way for changes in the outside world—such as a new version of a dependency being published—to automatically affect a Go build. > Unlike most other package managers files, Go modules don’t have a separate list of constraints and a lock file pinning specific versions. The version of every dependency contributing to any Go build is fully determined by the go.mod file of the main module. I don't know if thi…
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…
How Go mitigates supply chain attacks
121–130 of 265 posts
Re: How Go mitigates supply chain attacks
#122Earlier quoted context omitted.
That still implies that I need to know to update the version constraint of what may be a very deep transitive dependency, doesn't it?
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…
> 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.mod file are hand-maintained and need to be preserved and which things were filled in implicitly by the tool traversing dependency graphs?
> Repeatedly assuming that the Go core team never thought through the design of Go Modules
I'm certainly not assuming that. But I'm also not assuming they found a perfect solution to package management that all other package management tools failed to find. What's more likely is that they chose a different set of trade-offs, which is what this thread is exploring.
Re: How Go mitigates supply chain attacks
#123Earlier quoted context omitted.
Go just expects you to manually trigger the updates. Thats all. It still is in favor of updating to take security fixes, so i think your argument is wrong.
Let's say my_app uses package foo which uses package bar. It turns out there is a security bug in bar. The bar maintainers release a patch version that fixes it. In most package managers, users of my_app can and will get that fix with no work on the part of the author of foo. I'm not very familiar with Go's approach but I thought that unless foo's author puts out a version of foo that bumps its minimum version depend…
It's more complicated in general, with diamond dependencies. There needs to be a chain of module updates between you and foo, with the minimum case being a chain of length one where you specify the version of foo directly.
So, people do need to pay attention to security patch announcements. But popular modules, at least, are likely to be get updated relatively quickly, because only one side of a diamond dependency needs to notice and do a release.
Re: How Go mitigates supply chain attacks
#124Earlier quoted context omitted.
Kind of ironic for a language that was the first that I know about where you can straight up import libraries with a URL directly in your source.
It's not a URL, it's just an identifier that sort-of resembles a URL similar to Kubernetes annotation conventions.
Re: How Go mitigates supply chain attacks
#125Earlier quoted context omitted.
This is a pretty genuinely confounding response, and I mean that with absolutely no offense intended. There is a tremendous amount of fighting between devs who prefer Go and Rust, and a tremendous amount of elitism as well, truly from both perspectives. Rust gained a reputation for elitism long before Go did; “Rust Evangelism Strike Force” was never meant to be pejorative, and “Rewrite it in Rust” was never meant to…
Quoted post unavailable.
It thrives in infrastructure and web servers.
Re: How Go mitigates supply chain attacks
#126Earlier quoted context omitted.
I haven't used Go full-time, but I have used it on and off for more than a few years. There are certainly things I respect and appreciate about it, but there are also a lot of things that annoy me about it. Some that you might consider "superficial", I consider important. If I'm going to be spending all day in a language, I want the ergonomics of the language itself to work with me, not against me, and Go often does…
"but it also means that module publishers can pull versions (or the entire module) for arbitrary, selfish reasons, and then the community is left with a lot of difficulty" By default go get will download the source code into the pgk/mod folder. So if a module is pulled by the author, you can just use your copy of the source to fork it.
Re: How Go mitigates supply chain attacks
#127Earlier quoted context omitted.
Software programmer usually need to solve specific task, not worlds problems. If you go too generalized you will need 10x, 100x or 1000x more amount of time and code. Look at Big tech, this is what they are doing, they employ thousands workers that write millions of lines of code every day, only to make it work for every case in a world. And still can't compete with specialized solution.
> Software programmer usually need to solve specific task, not worlds problems. If you go too generalized you will need 10x, 100x or 1000x more amount of time and code. Yes, I accept this is true, but your earlier claim was much more specific: that using dependencies at all makes things worse. I gave you a specific example (GUI libraries) but you completely ignored it. How does your 0-dependencies theory survive an e…
Re: How Go mitigates supply chain attacks
#128Earlier quoted context omitted.
This is a pretty genuinely confounding response, and I mean that with absolutely no offense intended. There is a tremendous amount of fighting between devs who prefer Go and Rust, and a tremendous amount of elitism as well, truly from both perspectives. Rust gained a reputation for elitism long before Go did; “Rust Evangelism Strike Force” was never meant to be pejorative, and “Rewrite it in Rust” was never meant to…
Quoted post unavailable.
Of course, there’s degrees. To take an example, filenames. What is a filename? Is it a bag of bytes, a set of UNICODE codepoints, something more elaborate? No operating system agrees 100%. Go handles this by just simply not. It converts stuff to and from UTF-8 and if it doesn’t convert, you can’t use it. This is a limitation of Go programs that use Go’s built-in filesystem support (roughly all of them.)
That decision is a clear-cut simplification of reality. It can matter. However, the fact that it comes up so seldom is a reflection of reality: normally, you are totally able to make the concession that you only handle paths that are valid UTF-8 and nothing else. Go makes this concession on your behalf. It makes many such concessions, and it is documented, though not very well-known, because most developers don’t care. What most developers want is a language that makes sensible tradeoffs so that their programs can remain simple but still reasonable. In TYOOL 2022, I absolutely think it is reasonable that a program may impose valid UNICODE filenames as a requirement.
Rust handles it with OsStr, of course. Now OsStr is a good idea, but it pushes the decision for how to handle the problem further down. On one hand, this is great if you absolutely must handle ridiculous filenames that have essentially arbitrary bytes or old encodings in them, which, to be sure, is a real thing.
The file permissions stuff is similar. If Go is going to present a file permissions interface and then just fudge it, what’s the point of including it at all? Well, in my estimation, the point is that on POSIX systems, you can’t really avoid dealing with file permission bits. In most cases, when a Go program specifies mode bits, its doing so so that on POSIX, the program doesn’t write a file with permissions that don’t make sense, are unusable, or potentially even open up security issues. (You would not want user-uploaded data to go into a file with +x!) On Windows, it can usually be ignored, at least from the scope of the Go software. That’s, again, an opinionated choice. If I needed more granular control over permission, I would probably need more OS-specific code and interfaces anyways, something that is totally doable in Go.
So far Go is oversimplifying things in a very opinionated manner. And in fact, this means that some programs are difficult to write correctly in Go.
But, and here’s the kicker, I don’t usually want to write code that breaks the assumptions that Go makes and requires in its standard library. Even if I’m in Rust, if I want to write some utility code that deals with filenames, often times the string transformations I want to perform on the filename I will want to do in the space of valid UNICODE, because it’s simple and predictable. Even if all I want to do is append a suffix before the file extension, I still would prefer to work in valid UNICODE only. If I’m dealing with say, a broken Shift-JIS filename and want to append a suffix, even if I try to do the right thing and treat it as an ASCII compatible bag of bytes, I could wind up with an even more broken filename as a result, because the state of the filename before the suffix could corrupt the suffix.
The key difference is in perspective. “100% correct” is demonstrably unattainable. You can make more exact abstractions, but for sanity we make assumptions left and right that are “good enough.” You really shouldn’t use the user home directory or user profile directory of a given OS for much directly because it’s honestly a different directory with very different semantics per OS, yet plenty of Rust software (and Go software!) still does this anyways.
Meanwhile, while writing perfectly watertight code in Go is basically impossible, it’s also not a fair benchmark. Nothing does that. Rust is especially prone to stack overflows due to its lack of placement new, and it’s absolutely trivial to do this. Go doesn’t really have stack overflows at all, because it has a dynamically expanding stack. Cargo and its ecosystem has a problem with dependency hell; 200 nested dependencies to install a tool that counts lines of source code is a real thing that actually exists. Go’s ecosystem is definitely in better shape here, for reasons mentioned in the article that I wholeheartedly agree with. There are more things I could say that I don't like about Rust. I could possibly even fill an article about it, but I don't think that it's very productive. I think it's better to just acknowledge that most of these pain points are a direct result of the fact that any decision you make about language design and tooling will have knock-on effects down the ecosystem.
This article is also unfair in that it basically pins Go, a language which intentionally limits scope by taking these concessions, with Rust, a language that happily expands scope to try to cover more edge cases. Both choices are valid and offer different tradeoffs. However, there is a world of different programming languages, and Go is not the first one that implies limitations on filenames, for example. Hell, what happens if you write plain C code that tries to access broken Windows filenames? How much Win32 C code exists that doesn’t handle UNICODE or broken filenames? How many times have you tried to compile some code and had to move it to a directory without spaces in the filename because the program didn’t handle that?
Go is an opinionated language. If you don’t like its opinions, you won’t be happy with it. Orienting this as a correctness issue makes it seem like the Go developers made these tradeoffs haphazardly and without thought. I just flatly disagree.
Re: How Go mitigates supply chain attacks
#129Earlier quoted context omitted.
Let's say my_app uses package foo which uses package bar. It turns out there is a security bug in bar. The bar maintainers release a patch version that fixes it. In most package managers, users of my_app can and will get that fix with no work on the part of the author of foo. I'm not very familiar with Go's approach but I thought that unless foo's author puts out a version of foo that bumps its minimum version depend…
As I understand it, that's true in the simple case. If you have `my_app -> foo -> bar` then there's only one path to bar, and you only get a new bar when you upgrade foo. It's more complicated in general, with diamond dependencies. There needs to be a chain of module updates between you and foo, with the minimum case being a chain of length one where you specify the version of foo directly. So, people do need to pay…
This is not correct. You can update bar independent of foo directly from the top-level go.mod file in your project.