-
-
Notifications
You must be signed in to change notification settings - Fork 45
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
Add support for 1.4 to cyclonedx-bom #399
Conversation
Signed-off-by: Allie Sierra <[email protected]> Signed-off-by: tokcum <[email protected]>
Signed-off-by: tokcum <[email protected]>
…ng From, interim fix in specs.v1_3.component marked as todo Signed-off-by: tokcum <[email protected]>
* Update dependencies - base64 deprecated the functions, so replacing them with the equivalent engine - also update flake and formatting - fix new clippy lints - fix MSRV due to SPDX license change Signed-off-by: Amy Keibler <[email protected]> Signed-off-by: tokcum <[email protected]>
…cloneDX#365) * refactor: show a warning when a dependency cannot be resolved Signed-off-by: Jens Reimann <[email protected]> * fix: lookup the top level dependencies from the resolved graph Instead of using the declared dependency, this change now uses to the resolved dependency graph to look up the top level dependencies. The difference is, that a declared dependency may be overridden. And so the source no longer matches, which results in the dependency missing from the final SBOM. Using the resolved dependency graph, overriding dependencies is now covered as well. This also aligns with the "all dependencies" approach, which also uses the resolved dependencies. Closes: CycloneDX#364 Signed-off-by: Jens Reimann <[email protected]> * refactor: accept suggested changes from review Signed-off-by: Jens Reimann <[email protected]> Co-authored-by: Amy Keibler <[email protected]> Signed-off-by: Jens Reimann <[email protected]> Co-authored-by: Amy Keibler <[email protected]> Signed-off-by: tokcum <[email protected]>
Signed-off-by: tokcum <[email protected]>
…, component.version is Option in 1.4 Signed-off-by: tokcum <[email protected]>
Signed-off-by: tokcum <[email protected]>
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 18 to 20. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](cachix/install-nix-action@v18...v20) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: tokcum <[email protected]>
- Rust dependencies - Swap out "toml" for "toml_edit" due to change with "cargo" - Update the Nix flake Signed-off-by: Amelia Keibler <[email protected]> Signed-off-by: tokcum <[email protected]>
Bumps [xml-rs](https://github.com/kornelski/xml-rs) from 0.8.4 to 0.8.10. - [Changelog](https://github.com/kornelski/xml-rs/blob/main/Changelog.md) - [Commits](kornelski/xml-rs@0.8.4...0.8.10) --- updated-dependencies: - dependency-name: xml-rs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: tokcum <[email protected]>
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.160 to 1.0.163. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](serde-rs/serde@v1.0.160...v1.0.163) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: tokcum <[email protected]>
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.70 to 1.0.71. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](dtolnay/anyhow@1.0.70...1.0.71) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: tokcum <[email protected]>
👋 i'm interested in using SBOMs with some 1.4 features and was curious how things were coming along with 1.4 support/this PR? Are there things others can do to help or just waiting on more reviews? |
I would love to understand what the holdup is on this one? If help is needed, I am willing to. |
Hi, this PR has three aspects. First one is obviously to support 1.4. The second one is related to the ability to support more than one version. This involves some refactoring. I tried to keep this to a minimum. The third one is to add tests with real world SBOMs, e.g. from trivy. I already spent many hours on this and I'm willing to put more effort in. However, so far, I got absolutely no feedback from the maintainer. I didn't expect to get much advice. A confirmation that my approach is okay would suffice, something like "Looks good, go ahead!" or "Sorry, you have to find a different way for ... .". |
Hi @tokcum appreciate all the effort that went into this. Would it be possible to resolve the conflicts? I'd like to test locally prior to merging. |
@stevespringett, thank you for your support. Sure, I'll bring this into shape and update the PR. I've some free time at the end of Aug and early in Sept. |
Signed-off-by: tokcum <[email protected]>
Signed-off-by: tokcum <[email protected]>
Signed-off-by: tokcum <[email protected]>
Hi @stevespringett , it would be great if you find time for a quick review of this draft. I resolved all the conflicts and improved the integration testing differentiating between verification and validation. The approach is described in cyclonedx-bom/tests/README.md. I will continue to work on the parts of the 1.4 spec not yet supported, see cyclonedx-bom/tests/spec/1.4/not_yet_supported. Let me know what you think and how to proceed. Thank you. |
Signed-off-by: tokcum <[email protected]>
FYI @lfrancke |
Thank you! |
Signed-off-by: tokcum <[email protected]>
Great! I marked the PR as ready for review. Let me know what to improve. |
Signed-off-by: tokcum <[email protected]>
I'll take a look and try to get this reviewed on Monday. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some nits like outstanding TODOs and commented out code. I pointed them out where I noticed them. I don't see any major issues.
I appreciate the thorough testing against the spec, and the doc comments!
This is quite a lot of code! In the interest of being able to reproduce this for future versions, how was this file generated? This is a lot of code. Did you use templating of some sort, or is it entirely handwritten?
The big question here is whether we want to keep 1.3 support around, or remove it and go all in on 1.4 (and soon 1.5). I think supporting older versions would create too much of a maintenance burden, and I am not aware of compelling use cases them, so I prefer to gut 1.3 support. Is there a good reason to keep it?
cyclonedx-bom/src/models/bom.rs
Outdated
use crate::validation::{ | ||
FailureReason, Validate, ValidationContext, ValidationError, ValidationPathComponent, | ||
ValidationResult, | ||
}; | ||
use crate::xml::{FromXmlDocument, ToXml}; | ||
|
||
#[derive(Debug, PartialEq, Eq)] | ||
// todo: derive(Eq) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a TODO? Are there any blockers to actually doing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there was a detail I was not sure how to handle it appropriately. Spec 1.4 introduced a vulnerability rating with a decimal score. In rust this is a f32 which does not allow to derive Eq
.
Today I've worked on a solution. I'm not sure though, that my approach is okay. It would be great if you could check it and share your thoughts. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the approach you have taken. Thanks!
let bom: crate::specs::v1_3::bom::Bom = self.into(); | ||
let bom: crate::specs::v1_3::bom::Bom = self | ||
.try_into() | ||
.expect("todo: error handling in output_as_json_v1_3"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another unfulfilled TODO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've looked into this. I had to switch from into()
to try_into()
because of the different handling of version
in Component
in 1.3 vs. 1.4. In 1.3 it is mandatory, in 1.4 is is optional. So, a conversion into 1.3 might fail when the Component
has no version
.
The thing is that try_into
returns either Ok(Bom)
or Error(BomError)
, while output_as_json_v1_3()
returns a Ok(())
or a Error(JsonWriteError)
but not a Error(BomError)
.
So, how can we handle this. I see three options:
- We do not change the interface of
output_as_json_v1_3()
and if aBomError
occurs we write the error as a JSON document. - We do not change the interface of
output_as_json_v1_3()
and if aBomError
occurs we translate theBomError
somehow into aJsonWriteError
. - We change the interface of
output_as_json_v1_3()
, which would introduce a breaking change.
We face the same challenge for the output_as_xml_v1_3()
. The output_as_*_v1_4()
are not affected because there we are able to use into()
. However, we could also change it to try_into()
to align it.
Are there other options I'm not aware of and which approach should we follow? Please advice. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are making a field optional to match the spec, which is going to introduce breaking changes no matter if it is pub
or not. I think it's a good idea to change the signature to reflect reality while we're making breaking changes anyway.
I've looked at the types and BomError
represents the possible failure modes better than a hypothetical modification of JsonWriteError
, so let's go with option 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, great. I'll follow this approach. Thanks.
let bom: crate::specs::v1_3::bom::Bom = self.into(); | ||
let bom: crate::specs::v1_3::bom::Bom = self | ||
.try_into() | ||
.expect("todo: error handling in output_as_json_v1_3"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
// todo: validate vulnerabilities, not sure about which approach to take | ||
// see different approaches for services vs. properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another TODO. The validation seems to be implemented for vulnerabilities now, any blockers to actually wiring it up?
} | ||
|
||
/// Parse the input as a JSON document conforming to [version 1.4 of the specification](https://cyclonedx.org/docs/1.4/json/) | ||
pub fn parse_from_json_v1_4<R: std::io::Read>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not necessarily an issue with the code, could be my ignorance of the codebase and/or the format, but: is there any reason to keep the 1.3 methods around now that we have 1.4 ones? Isn't 1.4 a strict superset with no new mandatory fields, so that 1.3 data could be parsed by methods expecting 1.4?
// todo: 1.3 vs. 1.4 - this field is an Option in 1.4 | ||
pub version: Option<NormalizedString>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything to be done here? I don't think it should impact this struct.
Maybe validate that this field is present in the 1.3 parser but not 1.4?
/* | ||
pub(crate) fn try_convert_optional_vec<A, B: TryFrom<A, Error = BomError>>(value: Option<Vec<A>>) -> Option<Result<Vec<B>, BomError>> { | ||
value.map(try_convert_vec) | ||
} | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commented out code, here and further down in this file. If it is not used, it should be removed - storing older code is what git is for.
Out of scope of this PR, but a note to self: We should also exclude the test folder from publishing to crates.io now that it is almost 11MB worth of snapshots, and start tagging releases in the repo (so that Linux distributions could still package it with tests). But our restricted access to the repo may make it harder. |
Yes. In fact, this PR does not convert |
Thanks. I'll do a survey on the CycloneDX slack what other libraries do. There's arguments for both sides Oh the other hand the versions did avoid breaking changes so far I believe. |
There is a good discussion about this in Slack now: https://cyclonedx.slack.com/archives/CV062H2GH/p1696319537238529 Summary:
What does it mean for us? I'd rather have a version of this library with 1.4 support this month than a version that supports multiple CycloneDX specs in three months. |
The summary of the technical considerations is:
So there are use cases for writing in older format versions, to be compatible with older software. But not so much for reading from older format versions, since any 1.3 document can be loaded by a 1.5 parser. |
|
||
## Sign off your commits | ||
|
||
Please sign off your commits, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated text snippet:
Please sign off your commits, to show that you agree to publish your changes under the current terms and licenses of the project
, and to indicate agreement with [Developer Certificate of Origin (DCO)](https://developercertificate.org/).
there are exceptions, especially bug fixes. For example CycloneDX/specification#204: there was a bug in the CycloneDX json schema < 1.5 which allowed a mix of multiple licenses.
Since this wrong behavior was allowed in CDX 1.3 json, there might exist documents that are not valid CDX 1.5 json - for obvious reasons. |
Hi @Shnatsel, thank you for the feedback. Great, that we get this going. I'll continue to work on it. To answer your questions, when I started to work on this I started by comparing the official specs of 1.3 vs. 1.4 to find out the differences. The result was that there were only a few really new things to consider. Most was "more of the same". Then I discussed the approach with @amy-keibler. She told my to keep the existing model part untouched as far as possible and add the code to support 1.4 under specs. Having said that, I copied specs/1.3 to specs/1.4 and started by implementing the "new" things in the official 1.4 spec. As I said, from an implementation perspective, most was not really new just more of the same. To be honest, at that time, many things were new to me, such as snapshot testing and how the existing code handled serialization and deserialization of XML. No templating behind the scenes, just a thoroughly done copy and paste job. Not very smart, sorry. Maybe we have to overhaul the approach at some point in the future. To answer your last question, the reason why I started to work on this is, I wanted to be able to deserialize SBOMs generated by trivy. At that time, trivy generated SBOMs in CycloneDX version 1.4. Meanwhile trivy uses 1.5. Anyway, I'm in charge of security at a software editor with many formerly created SBOMs which I would like to process with cyclonedx-bom. So, it would be great if cyclonedx-bom could support former versions as well. |
I reviewed my conversion with Amy and think that we should try to support multiple versions. nscur0 also mentioned the approach in the Slack discussion. I'm pretty confident we can make it and I think we should consider to add some tests to check the compatibility when serializing / de-serializing different versions. |
Thanks! |
Signed-off-by: tokcum <[email protected]>
Just a heads-up that this now has a few merge conflicts so you'd need to rebase it. |
FYI we'd like to get this merged in the next week or so, and then start on implementing v1.5. Please let me know if I can help in any way. |
Hi @tokcum, if we don't hear back from you we plan on taking this PR over the finish line at the beginning of next week. Thank you for all your work so far and being so patient. |
/// | ||
/// Defined via the [XML schema](https://cyclonedx.org/docs/1.4/xml/#type_ratingType) | ||
#[derive(Debug, PartialEq, Eq)] | ||
pub struct Score(i32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a small crate called ordered-float
that should cover this nicely. It provides implementations for Ord
& Eq
. The Score
struct then can look like:
#[derive(Debug, PartialEq, Eq)]
pub struct Score(OrderedFloat<f32>);
impl Score {
pub fn new_unchecked(score: f32) -> Self {
Score(score.into())
}
pub fn from_f32(score: f32) -> Option<Self> {
// or add range check here, maybe clamp between 0..=10?
Some(Score(score.into()))
}
pub fn to_f32(&self) -> f32 {
self.0.0
}
}
impl From<f32> for Score {
fn from(value: f32) -> Self {
Score(value.into())
}
}
impl From<Score> for f32 {
fn from(value: Score) -> f32 {
value.0.0
}
}
This simplifies conversions & should provide easier handling of scores.
It would be great to see that merged. Is there any case I could help? |
Someone from the team will pick this up soon (this week). There are just a few minor todos to handle and we believe the Snapshot tests don't necessarily pull their weight. I hope to see some progress here this or next week. |
Testing if this PR meets the expectations.