diff --git a/Cargo.lock b/Cargo.lock index c9da4f2..e814d5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ dependencies = [ [[package]] name = "auditable-cyclonedx" -version = "0.1.0" +version = "0.2.0" dependencies = [ "auditable-serde", "cyclonedx-bom", @@ -35,7 +35,7 @@ dependencies = [ [[package]] name = "auditable-info" -version = "0.9.0" +version = "0.10.0" dependencies = [ "auditable-extract", "auditable-serde", @@ -45,7 +45,7 @@ dependencies = [ [[package]] name = "auditable-serde" -version = "0.8.0" +version = "0.9.0" dependencies = [ "schemars", "semver", @@ -56,7 +56,7 @@ dependencies = [ [[package]] name = "auditable2cdx" -version = "0.1.0" +version = "0.1.1" dependencies = [ "auditable-cyclonedx", "auditable-info", @@ -110,7 +110,7 @@ dependencies = [ [[package]] name = "cargo-auditable" -version = "0.6.7" +version = "0.7.0" dependencies = [ "auditable-info", "auditable-serde", diff --git a/auditable-cyclonedx/Cargo.toml b/auditable-cyclonedx/Cargo.toml index a5ae1b7..509e3c1 100644 --- a/auditable-cyclonedx/Cargo.toml +++ b/auditable-cyclonedx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auditable-cyclonedx" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Sergey \"Shnatsel\" Davidoff "] license = "MIT OR Apache-2.0" @@ -12,4 +12,4 @@ categories = ["encoding"] [dependencies] cyclonedx-bom = "0.8.0" -auditable-serde = {version = "0.8.0", path = "../auditable-serde"} +auditable-serde = {version = "0.9.0", path = "../auditable-serde"} diff --git a/auditable-info/Cargo.toml b/auditable-info/Cargo.toml index 83b3534..378a5f4 100644 --- a/auditable-info/Cargo.toml +++ b/auditable-info/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auditable-info" -version = "0.9.0" +version = "0.10.0" authors = ["Sergey \"Shnatsel\" Davidoff "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-secure-code/cargo-auditable" @@ -13,7 +13,7 @@ edition = "2018" [dependencies] auditable-extract = {version = "0.3.4", path = "../auditable-extract", default-features = false } miniz_oxide = { version = "0.8.0", features = ["std"] } -auditable-serde = {version = "0.8.0", path = "../auditable-serde", optional = true} +auditable-serde = {version = "0.9.0", path = "../auditable-serde", optional = true} serde_json = { version = "1.0.57", optional = true } [features] diff --git a/auditable-serde/Cargo.toml b/auditable-serde/Cargo.toml index b51b473..9a34fda 100644 --- a/auditable-serde/Cargo.toml +++ b/auditable-serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auditable-serde" -version = "0.8.0" +version = "0.9.0" authors = ["Sergey \"Shnatsel\" Davidoff "] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-secure-code/cargo-auditable" diff --git a/auditable-serde/src/lib.rs b/auditable-serde/src/lib.rs index 2ba84f0..1b0adc2 100644 --- a/auditable-serde/src/lib.rs +++ b/auditable-serde/src/lib.rs @@ -36,6 +36,41 @@ use std::str::FromStr; #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct VersionInfo { pub packages: Vec, + /// Format revision. Identifies the data source for the audit data. + /// + /// Format revisions are **backwards compatible.** + /// If an unknown format is encountered, it should be treated as the highest known preceding format. + /// For example, if formats `0`, `1` and `8` are known, format `4` should be treated as if it's `1`. + /// + /// # Known formats + /// + /// ## 0 (or the field is absent) + /// + /// Generated based on the data provided by [`cargo metadata`](https://doc.rust-lang.org/cargo/commands/cargo-metadata.html). + /// + /// There are multiple [known](https://github.com/rust-lang/cargo/issues/7754) + /// [issues](https://github.com/rust-lang/cargo/issues/10718) with this data source, + /// leading to the audit data sometimes including more dependencies than are really used in the build. + /// + /// However, is the only machine-readable data source available on stable Rust as of v1.88. + /// + /// Additionally, this format incorrectly includes [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html) + /// and their dependencies as runtime dependencies while in reality they are build-time dependencies. + /// + /// ## 1 + /// + /// Same as 0, but correctly records proc-macros and their dependencies as build-time dependencies. + /// + /// May still include slightly more dependencies than are actually used, especially in workspaces. + /// + /// ## 8 + /// + /// Generated using Cargo's [SBOM precursor](https://doc.rust-lang.org/cargo/reference/unstable.html#sbom) as the data source. + /// + /// This data is highly accurate, but as of Rust v1.88 can only be generated using a nightly build of Cargo. + #[serde(default)] + #[serde(skip_serializing_if = "is_default")] + pub format: u32, } /// A single package in the dependency tree @@ -117,7 +152,7 @@ pub enum DependencyKind { Runtime, } -fn is_default(value: &T) -> bool { +pub(crate) fn is_default(value: &T) -> bool { let default_value = T::default(); value == &default_value } diff --git a/auditable-serde/src/validation.rs b/auditable-serde/src/validation.rs index 6c9695e..f551ed3 100644 --- a/auditable-serde/src/validation.rs +++ b/auditable-serde/src/validation.rs @@ -1,10 +1,13 @@ -use crate::{Package, VersionInfo}; +use crate::{is_default, Package, VersionInfo}; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt::Display}; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] -pub struct RawVersionInfo { +pub(crate) struct RawVersionInfo { pub packages: Vec, + #[serde(default)] + #[serde(skip_serializing_if = "is_default")] + pub format: u32, } pub enum ValidationError { @@ -36,6 +39,7 @@ impl TryFrom for VersionInfo { } else { Ok(VersionInfo { packages: v.packages, + format: v.format, }) } } @@ -99,6 +103,7 @@ mod tests { let pkg1 = dummy_package(1, false, vec![0]); let raw = RawVersionInfo { packages: vec![pkg0, pkg1], + format: 0, }; assert!(VersionInfo::try_from(raw).is_err()); } @@ -109,6 +114,7 @@ mod tests { let pkg1 = dummy_package(1, false, vec![]); let raw = RawVersionInfo { packages: vec![pkg0, pkg1], + format: 0, }; assert!(VersionInfo::try_from(raw).is_ok()); } diff --git a/auditable2cdx/Cargo.toml b/auditable2cdx/Cargo.toml index 87f3941..231c7e1 100644 --- a/auditable2cdx/Cargo.toml +++ b/auditable2cdx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auditable2cdx" -version = "0.1.0" +version = "0.1.1" edition = "2021" authors = ["Sergey \"Shnatsel\" Davidoff "] license = "MIT OR Apache-2.0" @@ -10,8 +10,8 @@ description = "Command-line tool to recover `cargo auditable` data in CycloneDX # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -auditable-info = {version = "0.9.0", path = "../auditable-info"} -auditable-cyclonedx = {version = "0.1.0", path = "../auditable-cyclonedx"} +auditable-info = {version = "0.10.0", path = "../auditable-info"} +auditable-cyclonedx = {version = "0.2.0", path = "../auditable-cyclonedx"} serde_json = {version = "1.0.114", features = ["preserve_order"] } # the feature is needed for workarounds module only [package.metadata.dist] diff --git a/cargo-auditable.schema.json b/cargo-auditable.schema.json index 9fbddfd..ec45e3a 100644 --- a/cargo-auditable.schema.json +++ b/cargo-auditable.schema.json @@ -1,101 +1,107 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://rustsec.org/schemas/cargo-auditable.json", - "title": "cargo-auditable schema", - "description": "Describes the `VersionInfo` JSON data structure that cargo-auditable embeds into Rust binaries.", - "type": "object", - "required": [ - "packages" - ], - "properties": { - "packages": { - "type": "array", - "items": { - "$ref": "#/definitions/Package" + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://rustsec.org/schemas/cargo-auditable.json", + "title": "cargo-auditable schema", + "description": "Describes the `VersionInfo` JSON data structure that cargo-auditable embeds into Rust binaries.", + "type": "object", + "required": [ + "packages" + ], + "properties": { + "format": { + "description": "Format revision. Identifies the data source for the audit data.\n\nFormat revisions are **backwards compatible.** If an unknown format is encountered, it should be treated as the highest known preceding format. For example, if formats `0`, `1` and `8` are known, format `4` should be treated as if it's `1`.\n\n# Known formats\n\n## 0 (or the field is absent)\n\nGenerated based on the data provided by [`cargo metadata`](https://doc.rust-lang.org/cargo/commands/cargo-metadata.html).\n\nThere are multiple [known](https://github.com/rust-lang/cargo/issues/7754) [issues](https://github.com/rust-lang/cargo/issues/10718) with this data source, leading to the audit data sometimes including more dependencies than are really used in the build.\n\nHowever, is the only machine-readable data source available on stable Rust as of v1.88.\n\nAdditionally, this format incorrectly includes [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html) and their dependencies as runtime dependencies while in reality they are build-time dependencies.\n\n## 1\n\nSame as 0, but correctly records proc-macros and their dependencies as build-time dependencies.\n\nMay still include slightly more dependencies than are actually used, especially in workspaces.\n\n## 8\n\nGenerated using Cargo's [SBOM precursor](https://doc.rust-lang.org/cargo/reference/unstable.html#sbom) as the data source.\n\nThis data is highly accurate, but as of Rust v1.88 can only be generated using a nightly build of Cargo.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "packages": { + "type": "array", + "items": { + "$ref": "#/definitions/Package" + } + } + }, + "definitions": { + "DependencyKind": { + "type": "string", + "enum": [ + "build", + "runtime" + ] + }, + "Package": { + "description": "A single package in the dependency tree", + "type": "object", + "required": [ + "name", + "source", + "version" + ], + "properties": { + "dependencies": { + "description": "Packages are stored in an ordered array both in the `VersionInfo` struct and in JSON. Here we refer to each package by its index in the array. May be omitted if the list is empty.", + "type": "array", + "items": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "kind": { + "description": "\"build\" or \"runtime\". May be omitted if set to \"runtime\". If it's both a build and a runtime dependency, \"runtime\" is recorded.", + "allOf": [ + { + "$ref": "#/definitions/DependencyKind" + } + ] + }, + "name": { + "description": "Crate name specified in the `name` field in Cargo.toml file. Examples: \"libc\", \"rand\"", + "type": "string" + }, + "root": { + "description": "Whether this is the root package in the dependency tree. There should only be one root package. May be omitted if set to `false`.", + "type": "boolean" + }, + "source": { + "description": "Currently \"git\", \"local\", \"crates.io\" or \"registry\". Designed to be extensible with other revision control systems, etc.", + "allOf": [ + { + "$ref": "#/definitions/Source" } + ] + }, + "version": { + "description": "The package's version in the [semantic version](https://semver.org) format.", + "type": "string" } + } }, - "definitions": { - "DependencyKind": { - "type": "string", - "enum": [ - "build", - "runtime" - ] + "Source": { + "description": "Serializes to \"git\", \"local\", \"crates.io\" or \"registry\". Designed to be extensible with other revision control systems, etc.", + "oneOf": [ + { + "type": "string", + "enum": [ + "CratesIo", + "Git", + "Local", + "Registry" + ] }, - "Package": { - "description": "A single package in the dependency tree", - "type": "object", - "required": [ - "name", - "source", - "version" - ], - "properties": { - "dependencies": { - "description": "Packages are stored in an ordered array both in the `VersionInfo` struct and in JSON. Here we refer to each package by its index in the array. May be omitted if the list is empty.", - "type": "array", - "items": { - "type": "integer", - "format": "uint", - "minimum": 0.0 - } - }, - "kind": { - "description": "\"build\" or \"runtime\". May be omitted if set to \"runtime\". If it's both a build and a runtime dependency, \"runtime\" is recorded.", - "allOf": [ - { - "$ref": "#/definitions/DependencyKind" - } - ] - }, - "name": { - "description": "Crate name specified in the `name` field in Cargo.toml file. Examples: \"libc\", \"rand\"", - "type": "string" - }, - "root": { - "description": "Whether this is the root package in the dependency tree. There should only be one root package. May be omitted if set to `false`.", - "type": "boolean" - }, - "source": { - "description": "Currently \"git\", \"local\", \"crates.io\" or \"registry\". Designed to be extensible with other revision control systems, etc.", - "allOf": [ - { - "$ref": "#/definitions/Source" - } - ] - }, - "version": { - "description": "The package's version in the [semantic version](https://semver.org) format.", - "type": "string" - } + { + "type": "object", + "required": [ + "Other" + ], + "properties": { + "Other": { + "type": "string" } - }, - "Source": { - "description": "Serializes to \"git\", \"local\", \"crates.io\" or \"registry\". Designed to be extensible with other revision control systems, etc.", - "oneOf": [ - { - "type": "string", - "enum": [ - "CratesIo", - "Git", - "Local", - "Registry" - ] - }, - { - "type": "object", - "required": [ - "Other" - ], - "properties": { - "Other": { - "type": "string" - } - }, - "additionalProperties": false - } - ] + }, + "additionalProperties": false } + ] } -} + } +} \ No newline at end of file diff --git a/cargo-auditable/Cargo.toml b/cargo-auditable/Cargo.toml index 957dd72..a6ba644 100644 --- a/cargo-auditable/Cargo.toml +++ b/cargo-auditable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-auditable" -version = "0.6.7" +version = "0.7.0" edition = "2021" authors = ["Sergey \"Shnatsel\" Davidoff "] license = "MIT OR Apache-2.0" @@ -14,7 +14,7 @@ readme = "../README.md" [dependencies] object = {version = "0.36", default-features = false, features = ["write"]} -auditable-serde = {version = "0.8.0", path = "../auditable-serde"} +auditable-serde = {version = "0.9.0", path = "../auditable-serde"} miniz_oxide = {version = "0.8.0"} serde_json = "1.0.57" cargo_metadata = "0.18" @@ -24,5 +24,5 @@ wasm-gen = "0.1.4" [dev-dependencies] cargo_metadata = "0.18" -auditable-info = {version = "0.9.0", path = "../auditable-info", features = ["wasm"]} +auditable-info = {version = "0.10.0", path = "../auditable-info", features = ["wasm"]} which = "4.3.0" diff --git a/cargo-auditable/src/auditable_from_metadata.rs b/cargo-auditable/src/auditable_from_metadata.rs index 3b06102..3c563dc 100644 --- a/cargo-auditable/src/auditable_from_metadata.rs +++ b/cargo-auditable/src/auditable_from_metadata.rs @@ -218,7 +218,10 @@ pub fn encode_audit_data( package.dependencies.sort_unstable(); } } - Ok(VersionInfo { packages }) + Ok(VersionInfo { + packages, + format: 1, + }) } fn strongest_dep_kind(deps: &[cargo_metadata::DepKindInfo]) -> PrivateDepKind { diff --git a/cargo-auditable/src/sbom_precursor.rs b/cargo-auditable/src/sbom_precursor.rs index 79a22a2..f6f8b4c 100644 --- a/cargo-auditable/src/sbom_precursor.rs +++ b/cargo-auditable/src/sbom_precursor.rs @@ -89,7 +89,10 @@ impl From for VersionInfo { } } - VersionInfo { packages } + VersionInfo { + packages, + format: 8, + } } } diff --git a/rust-audit-info/Cargo.lock b/rust-audit-info/Cargo.lock index 079414c..6acfacf 100644 --- a/rust-audit-info/Cargo.lock +++ b/rust-audit-info/Cargo.lock @@ -18,7 +18,7 @@ dependencies = [ [[package]] name = "auditable-info" -version = "0.9.0" +version = "0.10.0" dependencies = [ "auditable-extract", "miniz_oxide", diff --git a/rust-audit-info/Cargo.toml b/rust-audit-info/Cargo.toml index 300002b..c4458b4 100644 --- a/rust-audit-info/Cargo.toml +++ b/rust-audit-info/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -auditable-info = {version = "0.9.0", default-features = false, path = "../auditable-info"} +auditable-info = {version = "0.10.0", default-features = false, path = "../auditable-info"} [features] wasm = ["auditable-info/wasm"]