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}