Skip to main content

assay_core/mcp/
runtime_features.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5// ─────────────────────────────────────────────────────────────
6// Discovery Config
7// ─────────────────────────────────────────────────────────────
8
9#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
10pub struct DiscoveryConfig {
11    #[serde(default)]
12    pub enabled: bool,
13
14    #[serde(default = "default_discovery_methods")]
15    pub methods: Vec<DiscoveryMethod>,
16
17    pub output: Option<PathBuf>,
18
19    #[serde(default)]
20    pub on_findings: DiscoveryActions,
21}
22
23#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
24#[serde(rename_all = "snake_case")]
25pub enum DiscoveryMethod {
26    ConfigFiles,
27    Processes,
28    Network,
29    Dns,
30    WellKnown,
31}
32
33fn default_discovery_methods() -> Vec<DiscoveryMethod> {
34    vec![DiscoveryMethod::ConfigFiles, DiscoveryMethod::Processes]
35}
36
37#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
38pub struct DiscoveryActions {
39    #[serde(default)]
40    pub unmanaged_server: ActionLevel,
41
42    #[serde(default)]
43    pub no_auth: ActionLevel,
44}
45
46#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Default)]
47#[serde(rename_all = "snake_case")]
48pub enum ActionLevel {
49    #[default]
50    Log,
51    Warn,
52    Fail,
53}
54
55// ─────────────────────────────────────────────────────────────
56// Runtime Monitor Config
57// ─────────────────────────────────────────────────────────────
58
59#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
60pub struct RuntimeMonitorConfig {
61    #[serde(default)]
62    pub enabled: bool,
63
64    #[serde(default)]
65    pub provider: MonitorProvider,
66
67    /// SOTA Phase 5: Cgroup Scope Configuration
68    #[serde(default)]
69    pub scope: MonitorScopeConfig,
70
71    #[serde(default)]
72    pub rules: Vec<MonitorRule>,
73}
74
75#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
76#[serde(rename_all = "snake_case")]
77pub struct MonitorScopeConfig {
78    #[serde(default)]
79    pub mode: MonitorMode,
80
81    #[serde(default)]
82    pub cgroup: CgroupConfig,
83}
84
85impl Default for MonitorScopeConfig {
86    fn default() -> Self {
87        Self {
88            mode: MonitorMode::CgroupV2,
89            cgroup: CgroupConfig::default(),
90        }
91    }
92}
93
94#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Default)]
95#[serde(rename_all = "snake_case")]
96pub enum MonitorMode {
97    #[default]
98    CgroupV2,
99    PidSet, // Legacy
100}
101
102#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
103pub struct CgroupConfig {
104    #[serde(default = "default_true")]
105    pub freeze_on_incident: bool,
106
107    #[serde(default = "default_true")]
108    pub create_leaf: bool,
109
110    #[serde(default = "default_assay_prefix")]
111    pub name_prefix: String,
112
113    #[serde(default = "default_true")]
114    pub cleanup: bool,
115
116    #[serde(default = "default_pids_max")]
117    pub pids_max: u32,
118}
119
120impl Default for CgroupConfig {
121    fn default() -> Self {
122        Self {
123            freeze_on_incident: true,
124            create_leaf: true,
125            name_prefix: default_assay_prefix(),
126            cleanup: true,
127            pids_max: default_pids_max(),
128        }
129    }
130}
131
132fn default_assay_prefix() -> String {
133    "assay".to_string()
134}
135
136fn default_pids_max() -> u32 {
137    2048
138}
139
140#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
141#[serde(rename_all = "snake_case")]
142pub enum MonitorProvider {
143    #[default]
144    Ebpf,
145}
146
147/// If you already have a Severity type in assay-core: replace this with your existing Severity.
148#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
149#[serde(rename_all = "snake_case")]
150pub enum Severity {
151    Low,
152    #[default]
153    Medium,
154    High,
155    Critical,
156}
157
158#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
159pub struct MonitorRule {
160    pub id: String,
161
162    #[serde(rename = "type")]
163    pub rule_type: MonitorRuleType,
164
165    #[serde(rename = "match")]
166    pub match_config: MonitorMatch,
167
168    #[serde(default)]
169    pub severity: Severity,
170
171    #[serde(default)]
172    pub action: MonitorAction,
173}
174
175#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
176#[serde(rename_all = "snake_case")]
177pub enum MonitorRuleType {
178    FileOpen,
179    NetConnect,
180    ProcExec,
181}
182
183#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, Default)]
184pub struct MonitorMatch {
185    #[serde(default)]
186    pub path_globs: Vec<String>,
187
188    #[serde(default)]
189    pub dest_globs: Vec<String>,
190
191    #[serde(default)]
192    pub not: Option<Box<MonitorMatch>>,
193}
194
195#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Default)]
196#[serde(rename_all = "snake_case")]
197pub enum MonitorAction {
198    #[default]
199    Log,
200    Alert,
201    Deny,
202    TriggerKill,
203}
204
205// ─────────────────────────────────────────────────────────────
206// Kill Switch Config
207// ─────────────────────────────────────────────────────────────
208
209#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
210pub struct KillSwitchConfig {
211    #[serde(default = "default_true")]
212    pub enabled: bool,
213
214    #[serde(default)]
215    pub mode: KillMode,
216
217    #[serde(default)]
218    pub kill_scope: KillScope,
219
220    #[serde(default = "default_grace_period")]
221    pub grace_period_ms: u64,
222
223    #[serde(default = "default_true")]
224    pub kill_children: bool,
225
226    #[serde(default)]
227    pub capture_state: bool,
228
229    pub output_dir: Option<PathBuf>,
230
231    #[serde(default)]
232    pub triggers: Vec<KillTrigger>,
233}
234
235impl Default for KillSwitchConfig {
236    fn default() -> Self {
237        Self {
238            enabled: true,
239            mode: KillMode::Graceful,
240            kill_scope: KillScope::default(),
241            grace_period_ms: default_grace_period(),
242            kill_children: true,
243            capture_state: false,
244            output_dir: None,
245            triggers: Vec::new(),
246        }
247    }
248}
249
250#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Default)]
251#[serde(rename_all = "snake_case")]
252pub enum KillMode {
253    #[default]
254    Graceful,
255    Immediate,
256}
257
258#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Default)]
259#[serde(rename_all = "snake_case")]
260pub enum KillScope {
261    #[default]
262    Cgroup,
263    PidFd,
264    LegacyPid,
265}
266
267#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
268pub struct KillTrigger {
269    pub on_rule: String,
270
271    #[serde(default)]
272    pub mode: Option<KillMode>,
273}
274
275fn default_true() -> bool {
276    true
277}
278fn default_grace_period() -> u64 {
279    3000
280}