Skip to main content

rightkit_agent_engine_api_integrations/
plugin_signature.rs

1//! Per-plugin signature provenance records.
2
3use serde::{Deserialize, Serialize};
4use std::path::{Path, PathBuf};
5
6#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
7#[serde(rename_all = "lowercase")]
8pub enum SignatureStatus {
9    Signed,
10    Unsigned,
11    Unknown,
12}
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct PluginSignatureRecord {
17    pub status: SignatureStatus,
18    #[serde(default)]
19    pub signed_by: String,
20    #[serde(default)]
21    pub signing_public_key: String,
22    #[serde(default)]
23    pub signature: String,
24    #[serde(default)]
25    pub installed_at: String,
26    #[serde(default)]
27    pub install_source: String,
28}
29
30impl PluginSignatureRecord {
31    pub fn signed(
32        signed_by: String,
33        signing_public_key: String,
34        signature: String,
35        install_source: String,
36        now_iso: String,
37    ) -> Self {
38        Self {
39            status: SignatureStatus::Signed,
40            signed_by,
41            signing_public_key,
42            signature,
43            installed_at: now_iso,
44            install_source,
45        }
46    }
47
48    pub fn unsigned(install_source: String, now_iso: String) -> Self {
49        Self {
50            status: SignatureStatus::Unsigned,
51            signed_by: String::new(),
52            signing_public_key: String::new(),
53            signature: String::new(),
54            installed_at: now_iso,
55            install_source,
56        }
57    }
58}
59
60pub fn sidecar_path(plugin_install_dir: &Path) -> PathBuf {
61    plugin_install_dir.join(".signature.json")
62}
63
64pub fn read_record(plugin_install_dir: &Path) -> PluginSignatureRecord {
65    let path = sidecar_path(plugin_install_dir);
66    match std::fs::read_to_string(&path) {
67        Ok(raw) => serde_json::from_str(&raw).unwrap_or_else(|_| unknown_record()),
68        Err(_) => unknown_record(),
69    }
70}
71
72pub fn write_record(plugin_install_dir: &Path, record: &PluginSignatureRecord) {
73    let path = sidecar_path(plugin_install_dir);
74    match serde_json::to_string_pretty(record) {
75        Ok(json) => {
76            if let Err(error) = std::fs::write(&path, json) {
77                eprintln!(
78                    "[plugin-signature] failed to write provenance to {}: {error}",
79                    path.display()
80                );
81            }
82        }
83        Err(error) => eprintln!("[plugin-signature] serialise record failed: {error}"),
84    }
85}
86
87pub fn is_acceptable(record: &PluginSignatureRecord, policy_reject_unsigned: bool) -> bool {
88    match record.status {
89        SignatureStatus::Signed | SignatureStatus::Unknown => true,
90        SignatureStatus::Unsigned => !policy_reject_unsigned,
91    }
92}
93
94fn unknown_record() -> PluginSignatureRecord {
95    PluginSignatureRecord {
96        status: SignatureStatus::Unknown,
97        signed_by: String::new(),
98        signing_public_key: String::new(),
99        signature: String::new(),
100        installed_at: String::new(),
101        install_source: String::new(),
102    }
103}