Skip to main content

termivar_scanner/
post_exploitation.rs

1//! Quarantined post-assessment metadata records.
2//!
3//! ## Runtime scope
4//!
5//! - **Build:** opt-in via `platform-models`.
6//! - **Execution:** no repository runtime caller (not on the default scan path).
7//! - **Default `termivar scan`:** no.
8//! - **Support:** experimental/scaffold.
9//!
10//! See `docs/internals/runtime-map.md`.
11//!
12//! This historical namespace now contains neutral, caller-supplied metadata
13//! only. It provides no execution or registry service and defines no credential,
14//! payload, shell, session, persistence, or lateral-movement type. Its string
15//! fields are neither bounded nor redacted; hosts must supply opaque references
16//! and already-redacted summaries and must never place secrets in these records.
17
18use serde::{Deserialize, Serialize};
19
20/// An observation supplied by an assessment caller.
21///
22/// `Debug` and serialization preserve every string verbatim. The caller owns
23/// redaction, byte limits, and the choice of opaque evidence references.
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub struct AssessmentObservation {
26    pub observation_id: String,
27    pub assessment_id: String,
28    pub asset_id: String,
29    pub summary: String,
30    pub evidence_references: Vec<String>,
31    pub observed_at: u64,
32    pub disposition: ObservationDisposition,
33}
34
35/// Caller-supplied review disposition for an assessment observation.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37pub enum ObservationDisposition {
38    #[serde(rename = "unreviewed")]
39    Unreviewed,
40    #[serde(rename = "confirmed")]
41    Confirmed,
42    #[serde(rename = "dismissed")]
43    Dismissed,
44}
45
46impl ObservationDisposition {
47    pub fn as_str(self) -> &'static str {
48        match self {
49            Self::Unreviewed => "unreviewed",
50            Self::Confirmed => "confirmed",
51            Self::Dismissed => "dismissed",
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn observation_is_caller_supplied_metadata() {
62        let observation = AssessmentObservation {
63            observation_id: "observation-1".to_owned(),
64            assessment_id: "assessment-1".to_owned(),
65            asset_id: "asset-1".to_owned(),
66            summary: "Manual review requested".to_owned(),
67            evidence_references: vec!["evidence-1".to_owned()],
68            observed_at: 1_000,
69            disposition: ObservationDisposition::Unreviewed,
70        };
71
72        let encoded = serde_json::to_string(&observation).expect("record serializes");
73        let decoded: AssessmentObservation =
74            serde_json::from_str(&encoded).expect("record deserializes");
75
76        assert_eq!(decoded, observation);
77        assert_eq!(decoded.disposition.as_str(), "unreviewed");
78    }
79}