4 ms·
There is absolutely a good reason for version ranges: security updates. When I, the owner of an application, choose a library (libuseful 2.1.1), I think it's f
by trjordan 1y ago
There is absolutely a good reason for version ranges: security updates.
When I, the owner of an application, choose a library (libuseful 2.1.1), I think it's fine that the library author uses other libraries (libinsecure 0.2.0).
But in 3 months, libinsecure is discovered (surprise!) to be insecure. So they release libinsecure 0.2.1, because they're good at semver. The libuseful library authors, meanwhile, are on vacation because it's August.
I would like to update. Turns out libinsecure's vulnerability is kind of a big deal. And with fully hardcoded dependencies, I cannot, without some horrible annoying work like forking/building/repackaging libuseful. I'd much rather libuseful depend on libinsecure 0.2.*, even if libinsecure isn't terribly good at semver.
I would love software to be deterministically built. But as long as we have security bugs, the current state is a reasonable compromise.
- tonsky 1y agoIt’s totally fine in Maven, no need to rebuild or repackage anything. You just override version of libinsecure in your pom.xml and it uses the version you told it to
- zahlman 1y agoSo you... manually re-lock the parts you need to?
- aidenn0 1y agoDon't forget the part where Maven silently picks one version for you when there are transitive dependency conflicts (and no, it's not always the newest one).
- deredede 1y agoSure, I'm happy with locking the parts I need to lock. Why would I lock the parts I don't need to lock?
- skywhopper 1y agoBecause you can’t know which ones you “need” to lock.
- lenkite 1y agoYou can definitely know this. Use mvn dependency:tree -Dverbose Or use maven-enforcer-plugin to fail the build on conflicts.
- deredede 1y agoWhat if libinsecure 0.2.1 is the version that introduces the vulnerability, do you still want your application to pick up the update? I think the better model is that your package manager let you do exactly what you want -- override libuseful's dependency on libinsecure when building your app.
- trjordan 1y agoOf course there's no 0-risk version of any of this. But in my experience, bugs tend to get introduced with features, then slowly ironed out over patches and minor versions. I want no security bugs, but as a heuristic, I'd strongly prefer the latest patch version of all libraries, even without perfect guarantees. Code rots, and most versioning schemes are designed with that in mind.
- MarkusQ 1y agoExcept the only reason code "rots" is that the environment keeps changing as people chase the latest shiny thing. Moreover, it rots _faster_ once the assumption that everyone is going to constantly update get established, since it can be used to justify pushing non-working garbage, on the assumption "we'll fix it in an update". This may sound judgy, but at the heart it's intended to be descriptive: there are two roughly stable states, and both have their problems.
- PhilipRoman 1y agoSlightly off topic but we need to normalize the ability to patch external dependencies (especially transitive ones). Coming from systems like Yocto, it was mind boggling to see a company bugging the author of an open source library to release a new version to the package manager with a fix that they desperately needed. In binary package managers this kind of workflow seems like an afterthought.
- eitau_1 1y agonixpkgs shines especially bright in this exact scenario
- guhcampos 1y agoThe author hints very briefly that Semantic Version is a hint, not a guarantee, to which I agree - but then I think we should be insisting on library maintainers that semantic versioning *should* be a guarantee, and in the worst case scenario, boycott libraries that claim to be semantically versioned but don't do it in reality.
- oiWecsio 1y agoI don't understand why major.minor.patchlevel is a "hint". It had been an interface contract with shared libraries written in C when I first touched Linux, and that was 25+ years ago; way before the term "semantic version" was even invented (AFAICT).
- michaelt 1y agoImagine I make a library for loading a certain format of small, trusted configuration files. Some guy files a CVE against my library, saying it crashes if you feed it a large, untrusted file. I decide to put out a new version of the library, fixing the CVE by refusing to load conspicuously large files. The API otherwise remains unchanged. Is the new release a major, minor, or bugfix release? As I have only an approximate understanding of semantic versioning norms, I could go for any of them to be honest. Some other library authors are just as confused as me, which is why major.minor.patchlevel is only a hint.
- shwestrick 1y agoI like this example. The client who didn't notice a difference would probably call it a bugfix. The client whose software got ever-so-slightly more reliable probably would call it a minor update. The client whose software previously was loading large files (luckily) without issue would call it major, because now their software just doesn't work anymore.
- michaelt 1y agoIt's also an almost-real situation (although I wasn't the library developer involved) You can Google "YAMLException: The incoming YAML document exceeds the limit" - an error introduced in response to CVE-2022-38752 - to see what happens when a library introduces a new input size limit. What happened in that case is: the updated library bumps their version from 1.31 to 1.32; then a downstream application updates their dependencies, passes all tests, and updates their version from 9.3.8.0 to 9.3.9.0
- seniorsassycat 1y agoYeah, this felt like a gap in the article. You'd have to wait for every package to update from the bottom up before you could update you top levels to remove a risk (or you could patch in place, or override) But what if all the packages had automatic ci/cd, and libinsecure 0.2.1 is published, libuseful automatically tests a new version of itself that uses 0.2.1, and if it succeeds it publishes a new version. And consumers of libuseful do the same, and so on.
- CognitiveLens 1y agoThe automatic ci/cd suggestion sounds appealing, but at least in the NPM ecosystem, the depth of those dependencies would mean the top-level dependencies would constantly be incrementing. On the app developer side, it would take a lot of attention to figure when it's important to update top-level dependencies and when it's not.
- silverwind 1y agoCould aggregate the incrementing to for example 1 per day.
- skybrian 1y agoGo has a deterministic package manager and handles security bugs by letting library authors retract versions [1]. The 'go get' command will print a warning if you try to retrieve a retracted version. Then you can bump the version for that module at top level. You also have the option of ignoring it if you want to build the old version for some reason, such as testing the broken version. [1] https://go.dev/ref/mod#go-mod-file-retract https://go.dev/ref/mod#go-mod-file-retract