-
-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug: Pre-release versions not considered matching #172
Comments
Hm, I just found this comment in the code: // https://docs.npmjs.com/misc/semver#prerelease-tags
fn pre_tag_is_compatible(&self, ver: &Version) -> bool {
// If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will
// only be
// allowed to satisfy comparator sets if at least one comparator with the same
// [major,
// minor, patch] tuple also has a prerelease tag.
!ver.is_prerelease() ||
(self.major == ver.major && self.minor == Some(ver.minor) &&
self.patch == Some(ver.patch) && !self.pre.is_empty())
} That may be correct for version resolution, but not for a case like vulnerability checking. Maybe the |
Yes, it's not what we want for resolution, for sure. That being said, if it doesn't make it for resolution, how would it make it into a vulnerability? Wouldn't you want those checks to follow resolution? |
Hm, I can't quite follow. In cargo-audit, an advisory is published as a file containing the semver specifiers for versions where the vulnerability is patched. Example. Vulnerability checking is done by testing whether a certain version is included in the patched range or not. |
Right, so if I had |
No, it's the other way around. If a program has I'm not sure how checking is done for libraries without a lockfile, maybe @tarcieri knows more. |
|
@steveklabnik are there any news on this issue? Unfortunately it renders cargo audit useless for all projects that use a pre-release of hyper, since it reports a patched version as vulnerable... |
News is basically, "I have no time to prioritize work on SemVer right now, I hope to get back to it soon, the lack of 1.0 is really bugging me, but I'm also only human." |
That said, I am also still not 100% sure what this issue is saying; I haven't dug into the exact details, but not matching pre-releases is a feature, not a bug. |
I understand about the workload, that's no problem. If it's clear what needs to be done I might even be able to come up with a pull request. But regarding bug vs feature: Independently from package managers, why would a query for |
That is exactly why. release candidates should be opt-in. All major semver implementations I'm aware of work this way, for this reason. If we changed the API to let |
It sounds like the simplest option is to explicitly enumerate the prereleases that contain the fix in the advisory's patched versions. I can also see how it's entirely possible there'd be prereleases which don't contain a security fix despite there being a fix in the final release. |
@steveklabnik I think we're still not talking about the same thing 🙂 The concrete use case I mentioned above:
|
(this is how the vulnerability db registers fixes)
Ah ha! This must be the misunderstanding. So the vulnerability DB uses a different conception of versions than Cargo does? You’re not expecting someone who puts >= 0.10.2 in their Cargo.toml to get the fix?
|
According to @tarcieri:
This means that we're always dealing with a pinned, concrete version from Cargo.lock, not with version ranges. For example, the question could be: "Is version |
Okay, we're finally on the same page. Whew. Thanks :) What do you think about
|
If there were a mode where prereleases were in lexographic order between the release versions, you could do e.g. |
@steveklabnik do you think this is something |
Here's the current logic we're using to select vulnerable crates, for what it's worth: self.find_by_crate(crate_name)
.iter()
.filter(|advisory| {
!advisory
.patched_versions
.iter()
.any(|req| req.matches(version))
})
.map(|a| *a)
.collect()
Perhaps there should be some special case handling of prereleases here? |
@tarcieri I think Maybe you need something more complicated to handle regressions, eg. |
Though the fact that Version's ordering doesn't match VersionReq's ordering is also a source of confusion. I think I'd expect semver's Version ordering to be consistent throughout the crate. |
It is definitely not that easy. Here are things, based empirically on vulns in the advisory DB, that alone fails to capture:
Here are some real-world
I am going to attempt to solve this by making a modified |
RustSec PR which vendors a minified/modified set of the |
I ended up ripping out the vendored code from https://github.com/RustSec/rustsec-crate/pull/217 We've managed to upgrade the (Sidebar: in general I've found the way prereleases are handled by this crate somewhat confusing) |
I still think VersionReq is the wrong type for this: it's a version requirement whereas what rustsec needs is a version range. Of your examples, all can be represented by a let patched_versions: Vec<Range<Version>> = ...;
patched_versions.iter().any(|range| range.contains(locked_version)) |
As it were, it seems some of the requirements we have no longer parse with
|
Right, that's why I modelled it as a |
Can you state concretely what you think the difference is? Ultimately we’re trying to test whether a concrete As a counterpoint, I can state what I feel is weird about the current prerelease handling: given a requirement for |
Sure: version requirements are about compatibility and stability of the API. I would expect A version range is about the development timeline. It makes perfect sense to say that versions |
Note that we also leverage version requirements as part of We can consider implementing our own version requirement type which differs from the one in this crate (again, the previous one was largely copy paste from semver v0.9.0), but IMO we are very much looking for a "version requirement" which satisfies a set of constraints. Semantically "version requirement" seems correct to me, even if the precise semantics of what RustSec needs and what SemVer ordinarily needs differ.
I think it would be more sense if prereleases weren't grouped into either category and considered distinct from a SemVer perspective, lexicographically located in between A major notable drawback of a |
Node.js's security working group also use |
I just noticed something else impacted by this issue: https://deps.rs thinks prerelease dependencies are out-of-date |
I'll go ahead and close this because I believe this matching behavior is intentional as described by section 5 of https://github.com/semver/semver/blob/efcff2c838c9945f79bfd21c1df0073271bcc29c/ranges.md. As of 1.0.0 though, the VersionReq's operators/comparators are publicly exposed and so it's possible to implement "nontraditional" matching logic downstream. https://docs.rs/semver/1.0.0-rc.1/semver/struct.VersionReq.html |
For reasons unrelated to this issue, we'll be migrating away from The rationale for the criteria in the spec all center around something which does make sense: making sure that prereleases don't match as a sort of boundary condition in a "less than" expression. As it were, I opened a separate issue related to that: #236. But this leads to this rather confusing behavior for anything other than that particular edge case... For the match expression
Of the prereleases returning |
Locking because this is not the right/productive place to rant about the spec, especially not about deliberate and sensible features of the spec. |
This test fails:
It matches the req if I remove the "-pre.0" suffix though.
This causes cargo-audit to report wrong vulnerabilities.
I assume this is a bug, and does not follow the spec, right?
The text was updated successfully, but these errors were encountered: