use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::{StringOrBool, deserialize_string_or_bool_opt};
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema, PartialEq)]
#[serde(default, deny_unknown_fields)]
pub struct AttestationConfig {
pub enabled: bool,
pub mode: Option<AttestationMode>,
pub artifacts: Option<Vec<AttestationArtifactKind>>,
#[serde(deserialize_with = "deserialize_string_or_bool_opt", default)]
pub skip: Option<StringOrBool>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AttestationMode {
Subjects,
Emit,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum AttestationArtifactKind {
Archive,
Binary,
Checksum,
Package,
Source,
Sbom,
Installer,
}
impl AttestationConfig {
pub const SUBJECTS_MANIFEST_NAME: &'static str = "attestation-subjects.json";
pub const STATEMENT_NAME: &'static str = "attestation.intoto.jsonl";
pub fn resolved_mode(&self) -> AttestationMode {
self.mode.unwrap_or(AttestationMode::Subjects)
}
pub fn resolved_artifacts(&self) -> Option<Vec<AttestationArtifactKind>> {
self.artifacts.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_mode_is_subjects() {
let cfg = AttestationConfig::default();
assert_eq!(cfg.resolved_mode(), AttestationMode::Subjects);
}
#[test]
fn omitted_artifacts_resolve_to_none_meaning_attest_everything() {
let cfg = AttestationConfig::default();
assert_eq!(cfg.resolved_artifacts(), None);
}
#[test]
fn parses_newly_selectable_kinds() {
let yaml = "enabled: true\nartifacts: [package, source, sbom, installer]\n";
let cfg: AttestationConfig = serde_yaml_ng::from_str(yaml).expect("parse");
assert_eq!(
cfg.resolved_artifacts(),
Some(vec![
AttestationArtifactKind::Package,
AttestationArtifactKind::Source,
AttestationArtifactKind::Sbom,
AttestationArtifactKind::Installer,
])
);
}
#[test]
fn default_is_disabled() {
assert!(!AttestationConfig::default().enabled);
}
#[test]
fn parses_yaml_with_explicit_mode_and_artifacts() {
let yaml = "enabled: true\nmode: emit\nartifacts: [archive, binary]\n";
let cfg: AttestationConfig = serde_yaml_ng::from_str(yaml).expect("parse");
assert!(cfg.enabled);
assert_eq!(cfg.resolved_mode(), AttestationMode::Emit);
assert_eq!(
cfg.resolved_artifacts(),
Some(vec![
AttestationArtifactKind::Archive,
AttestationArtifactKind::Binary
])
);
}
#[test]
fn rejects_unknown_field() {
let yaml = "enabled: true\nbogus: 1\n";
assert!(serde_yaml_ng::from_str::<AttestationConfig>(yaml).is_err());
}
#[test]
fn rejects_unknown_mode() {
let yaml = "enabled: true\nmode: sideways\n";
assert!(serde_yaml_ng::from_str::<AttestationConfig>(yaml).is_err());
}
}