Skip to main content

socket_patch_core/vex/
mod.rs

1//! OpenVEX 0.2.0 document generation from a Socket Patch manifest.
2//!
3//! Self-contained so it can be lifted into its own crate later. The
4//! module is organized as:
5//!
6//! * [`schema`] — hand-rolled OpenVEX 0.2.0 serde structs.
7//! * [`build`] — manifest + applied-set → [`schema::Document`].
8//! * [`product`] — auto-detect the top-level product PURL from the
9//!   filesystem (package.json / pyproject.toml / Cargo.toml).
10//! * [`verify`] — partition manifest entries by on-disk hash check.
11//! * [`time`] — minimal RFC 3339 timestamp formatter (no chrono).
12//!
13//! Cross-references against the Go reference implementation
14//! (<https://github.com/openvex/go-vex>) live next to the affected
15//! struct in [`schema`].
16
17pub mod build;
18pub mod product;
19pub mod schema;
20pub mod time;
21pub mod verify;
22
23pub use build::{build_document, BuildOptions};
24pub use product::{detect_product, DetectResult};
25pub use schema::{
26    Document, Justification, Product, Statement, Status, Subcomponent, Vulnerability,
27    OPENVEX_CONTEXT_V0_2_0,
28};
29pub use verify::{applied_patches, FailedPatch, VerifyOutcome};
30
31#[cfg(test)]
32mod conformance_tests;
33
34#[cfg(test)]
35mod reexport_tests {
36    //! Compile-only smoke tests for the public surface. If a future
37    //! refactor drops a `pub use` line, this module will fail to
38    //! compile — the visible symptom we want.
39
40    use super::*;
41
42    #[test]
43    fn every_reexport_is_usable_from_vex_namespace() {
44        // Names — just touching each one keeps the linker honest.
45        let _: &str = OPENVEX_CONTEXT_V0_2_0;
46
47        // Types instantiable via Default or struct literal.
48        let _ = DetectResult::default();
49        let _ = VerifyOutcome::default();
50        let _ = FailedPatch {
51            purl: String::new(),
52            reason: String::new(),
53        };
54        let _ = BuildOptions {
55            product_id: String::new(),
56            doc_id: String::new(),
57            author: String::new(),
58            tooling: None,
59        };
60        let _ = Vulnerability {
61            name: "GHSA-x".to_string(),
62            aliases: Vec::new(),
63        };
64        let _ = Subcomponent {
65            id: "pkg:npm/x@1".to_string(),
66            identifiers: None,
67            hashes: None,
68        };
69        let _ = Product {
70            id: "pkg:npm/app@1.0".to_string(),
71            identifiers: None,
72            hashes: None,
73            subcomponents: Vec::new(),
74        };
75        let _ = Statement {
76            id: None,
77            vulnerability: Vulnerability {
78                name: "GHSA-x".to_string(),
79                aliases: Vec::new(),
80            },
81            timestamp: None,
82            last_updated: None,
83            products: Vec::new(),
84            status: Status::NotAffected,
85            supplier: None,
86            justification: Some(Justification::InlineMitigationsAlreadyExist),
87            impact_statement: None,
88            action_statement: None,
89        };
90        let _ = Document {
91            context: OPENVEX_CONTEXT_V0_2_0.to_string(),
92            id: String::new(),
93            author: String::new(),
94            role: None,
95            timestamp: String::new(),
96            last_updated: None,
97            version: 1,
98            tooling: None,
99            statements: Vec::new(),
100        };
101
102        // Functions — reference them so an accidental rename
103        // surfaces here. We can't easily type async fns with
104        // reference parameters as `fn(_)` pointers (the lifetime
105        // bound goes through the returned future), so just take
106        // their address and discard it; the resolver will error if
107        // the symbol disappears.
108        let _ = build_document as *const ();
109        let _ = detect_product as *const ();
110        let _ = applied_patches as *const ();
111    }
112}