Skip to main content

nexo_ext_installer/
verify_error.rs

1//! Error variants returned by the cosign signature
2//! verification pipeline.
3
4use std::path::PathBuf;
5
6/// Errors surfaced by [`crate::verify::verify_plugin_signature`]
7/// and the `trusted_keys.toml` loader.
8#[derive(Debug, thiserror::Error)]
9pub enum VerifyError {
10    /// `cosign` binary not found on PATH or in any well-known
11    /// location, and no `cosign_binary` override was configured.
12    #[error("cosign binary not found (searched: {searched:?}). Install via brew/apt/dnf.")]
13    CosignNotFound {
14        /// Paths the discoverer probed.
15        searched: Vec<PathBuf>,
16    },
17
18    /// `cosign verify-blob` exited with a non-zero status.
19    #[error("cosign verify-blob exited non-zero: {stderr}")]
20    CosignFailed {
21        /// stderr captured from the cosign invocation. Preserved
22        /// verbatim so operators can diagnose identity mismatches
23        /// or transparency-log failures.
24        stderr: String,
25    },
26
27    /// IO error during signature/cert/bundle download or local
28    /// process spawning.
29    #[error("verify io error: {0}")]
30    Io(String),
31
32    /// Trust policy is `Require` but the resolved release has no
33    /// `.sig` + `.cert` assets.
34    #[error(
35        "trust policy requires signature for `{owner}`, but release has no .sig + .cert assets"
36    )]
37    PolicyRequiresSig {
38        /// Repo owner that the policy resolved against.
39        owner: String,
40    },
41
42    /// Release ships one half of the signing material but not the
43    /// other (e.g. `.sig` without `.cert`). Implies a publish
44    /// convention violation upstream; rejected outside `Ignore`.
45    #[error("release has `{present}` but missing companion `{missing}`")]
46    AssetIncomplete {
47        /// Asset suffix that was present (e.g. `.sig`).
48        present: &'static str,
49        /// Asset suffix expected as companion (e.g. `.cert`).
50        missing: &'static str,
51    },
52
53    /// `trusted_keys.toml` failed to parse.
54    #[error("trusted_keys.toml at `{path}` invalid: {reason}")]
55    TrustedKeysParse {
56        /// Path on disk where the file was loaded from.
57        path: PathBuf,
58        /// Parse failure reason.
59        reason: String,
60    },
61
62    /// One of the `[[authors]]` entries declared an
63    /// `identity_regexp` that does not compile under the Rust
64    /// regex engine.
65    #[error("identity_regexp `{got}` invalid: {reason}")]
66    IdentityRegexpInvalid {
67        /// Offending regex string.
68        got: String,
69        /// `regex::Regex::new` error message.
70        reason: String,
71    },
72
73    /// PEM/DER certificate could not be parsed.
74    #[error("certificate parse failed: {0}")]
75    CertParseFailed(String),
76
77    /// Certificate is well-formed but does not carry an
78    /// ECDSA-P256 public key — the only key type cosign uses
79    /// today.
80    #[error("certificate public key is not ECDSA-P256: {0}")]
81    UnsupportedKey(String),
82
83    /// Signature file could not be base64-decoded or DER-parsed.
84    #[error("signature decode failed: {0}")]
85    SignatureDecodeFailed(String),
86
87    /// Signature did not verify against the certificate's public
88    /// key over `SHA-256(blob)`. Either the blob, the
89    /// certificate, or the signature has been tampered with.
90    #[error("signature does not verify against the certificate")]
91    SignatureMismatch,
92
93    /// Certificate has no Subject Alternative Name URIs / emails
94    /// — Fulcio always emits at least one, so an empty SAN
95    /// almost certainly means we got handed the wrong cert.
96    #[error("certificate has no Subject Alternative Name entries")]
97    IdentityNotFound,
98
99    /// Certificate's SAN entries do not match the policy's
100    /// `identity_regexp`.
101    #[error("identity `{found}` does not match policy regex `{expected_regex}`")]
102    IdentityMismatch {
103        /// First SAN entry the verifier inspected (URIs/emails
104        /// joined for diagnostic clarity).
105        found: String,
106        /// Regex from the matched author entry.
107        expected_regex: String,
108    },
109
110    /// Certificate's Fulcio OIDC-issuer extension does not match
111    /// the policy.
112    #[error("OIDC issuer mismatch: cert claims `{found}`, policy requires `{expected}`")]
113    IssuerMismatch {
114        /// What the cert's `1.3.6.1.4.1.57264.1.{1,8}` extension
115        /// reported.
116        found: String,
117        /// What the trust policy required.
118        expected: String,
119    },
120}