osquery_rust_ng/plugin/table/
mod.rs

1pub(crate) mod column_def;
2pub use column_def::ColumnDef;
3pub use column_def::ColumnType;
4
5pub(crate) mod query_constraint;
6#[allow(unused_imports)]
7pub use query_constraint::QueryConstraints;
8
9use crate::_osquery::{
10    osquery, ExtensionPluginRequest, ExtensionPluginResponse, ExtensionResponse, ExtensionStatus,
11};
12use crate::plugin::ExtensionResponseEnum::SuccessWithId;
13use crate::plugin::_enums::response::ExtensionResponseEnum;
14use crate::plugin::{OsqueryPlugin, Registry};
15use enum_dispatch::enum_dispatch;
16use serde_json::Value;
17use std::collections::BTreeMap;
18use std::sync::{Arc, Mutex};
19
20#[derive(Clone)]
21#[enum_dispatch(OsqueryPlugin)]
22pub enum TablePlugin {
23    Writeable(Arc<Mutex<dyn Table>>),
24    Readonly(Arc<dyn ReadOnlyTable>),
25}
26
27impl TablePlugin {
28    pub fn from_writeable_table<R: Table>(table: R) -> Self {
29        TablePlugin::Writeable(Arc::new(Mutex::new(table)))
30    }
31
32    pub fn from_readonly_table<R: ReadOnlyTable>(table: R) -> Self {
33        TablePlugin::Readonly(Arc::new(table))
34    }
35}
36
37impl OsqueryPlugin for TablePlugin {
38    fn name(&self) -> String {
39        match self {
40            TablePlugin::Writeable(table) => {
41                let Ok(table) = table.lock() else {
42                    return "unable-to-get-table-name".to_string();
43                };
44
45                table.name()
46            }
47            TablePlugin::Readonly(table) => table.name(),
48        }
49    }
50
51    fn registry(&self) -> Registry {
52        Registry::Table
53    }
54
55    fn routes(&self) -> ExtensionPluginResponse {
56        let mut resp = ExtensionPluginResponse::new();
57
58        let columns = match self {
59            TablePlugin::Writeable(table) => {
60                let Ok(table) = table.lock() else {
61                    log::error!("Plugin was unavailable, could not lock table");
62                    return resp;
63                };
64
65                table.columns()
66            }
67            TablePlugin::Readonly(table) => table.columns(),
68        };
69
70        for column in &columns {
71            let mut r: BTreeMap<String, String> = BTreeMap::new();
72
73            r.insert("id".to_string(), "column".to_string());
74            r.insert("name".to_string(), column.name());
75            r.insert("type".to_string(), column.t());
76            r.insert("op".to_string(), column.o());
77
78            resp.push(r);
79        }
80
81        resp
82    }
83
84    fn ping(&self) -> ExtensionStatus {
85        ExtensionStatus::default()
86    }
87
88    fn handle_call(&self, request: crate::_osquery::ExtensionPluginRequest) -> ExtensionResponse {
89        let action = request.get("action").map(|s| s.as_str()).unwrap_or("");
90
91        log::trace!("Action: {action}");
92
93        match action {
94            "columns" => {
95                let resp = self.routes();
96                ExtensionResponse::new(
97                    osquery::ExtensionStatus {
98                        code: Some(0),
99                        message: Some("Success".to_string()),
100                        uuid: Default::default(),
101                    },
102                    resp,
103                )
104            }
105            "generate" => self.generate(request),
106            "update" => self.update(request),
107            "delete" => self.delete(request),
108            "insert" => self.insert(request),
109            _ => ExtensionResponseEnum::Failure(format!(
110                "Invalid table plugin action:{action:?} request:{request:?}"
111            ))
112            .into(),
113        }
114    }
115
116    fn shutdown(&self) {
117        log::trace!("Shutting down plugin: {}", self.name());
118
119        match self {
120            TablePlugin::Writeable(table) => {
121                let Ok(table) = table.lock() else {
122                    log::error!("Plugin was unavailable, could not lock table");
123                    return;
124                };
125
126                table.shutdown();
127            }
128            TablePlugin::Readonly(table) => table.shutdown(),
129        }
130    }
131}
132
133impl TablePlugin {
134    fn generate(&self, req: ExtensionPluginRequest) -> ExtensionResponse {
135        match self {
136            TablePlugin::Writeable(table) => {
137                let Ok(table) = table.lock() else {
138                    return ExtensionResponseEnum::Failure(
139                        "Plugin was unavailable, could not lock table".to_string(),
140                    )
141                    .into();
142                };
143
144                table.generate(req)
145            }
146            TablePlugin::Readonly(table) => table.generate(req),
147        }
148    }
149
150    fn update(&self, req: ExtensionPluginRequest) -> ExtensionResponse {
151        let TablePlugin::Writeable(table) = self else {
152            return ExtensionResponseEnum::Readonly().into();
153        };
154
155        let Ok(mut table) = table.lock() else {
156            return ExtensionResponseEnum::Failure(
157                "Plugin was unavailable, could not lock table".to_string(),
158            )
159            .into();
160        };
161
162        let Some(id) = req.get("id") else {
163            return ExtensionResponseEnum::Failure("Could not deserialize the id".to_string())
164                .into();
165        };
166
167        let Ok(id) = id.parse::<u64>() else {
168            return ExtensionResponseEnum::Failure("Could not parse the id".to_string()).into();
169        };
170
171        let Some(json_value_array) = req.get("json_value_array") else {
172            return ExtensionResponseEnum::Failure(
173                "Could not deserialize the json_value_array".to_string(),
174            )
175            .into();
176        };
177
178        // "json_value_array": "[1,\"lol\"]"
179        let Ok(row) = serde_json::from_str::<Value>(json_value_array) else {
180            return ExtensionResponseEnum::Failure(
181                "Could not parse the json_value_array".to_string(),
182            )
183            .into();
184        };
185
186        match table.update(id, &row) {
187            UpdateResult::Success => ExtensionResponseEnum::Success().into(),
188            UpdateResult::Constraint => ExtensionResponseEnum::Constraint().into(),
189            UpdateResult::Err(err) => ExtensionResponseEnum::Failure(err).into(),
190        }
191    }
192
193    fn delete(&self, req: ExtensionPluginRequest) -> ExtensionResponse {
194        let TablePlugin::Writeable(table) = self else {
195            return ExtensionResponseEnum::Readonly().into();
196        };
197
198        let Ok(mut table) = table.lock() else {
199            return ExtensionResponseEnum::Failure(
200                "Plugin was unavailable, could not lock table".to_string(),
201            )
202            .into();
203        };
204
205        let Some(id) = req.get("id") else {
206            return ExtensionResponseEnum::Failure("Could not deserialize the id".to_string())
207                .into();
208        };
209
210        let Ok(id) = id.parse::<u64>() else {
211            return ExtensionResponseEnum::Failure("Could not parse the id".to_string()).into();
212        };
213
214        match table.delete(id) {
215            DeleteResult::Success => ExtensionResponseEnum::Success().into(),
216            DeleteResult::Err(e) => {
217                ExtensionResponseEnum::Failure(format!("Plugin error {e}").to_string()).into()
218            }
219        }
220    }
221
222    fn insert(&self, req: ExtensionPluginRequest) -> ExtensionResponse {
223        let TablePlugin::Writeable(table) = self else {
224            return ExtensionResponseEnum::Readonly().into();
225        };
226
227        let Ok(mut table) = table.lock() else {
228            return ExtensionResponseEnum::Failure(
229                "Plugin was unavailable, could not lock table".to_string(),
230            )
231            .into();
232        };
233
234        let auto_rowid = req.get("auto_rowid").unwrap_or(&"false".to_string()) == "true";
235
236        let Some(json_value_array) = req.get("json_value_array") else {
237            return ExtensionResponseEnum::Failure(
238                "Could not deserialize the json_value_array".to_string(),
239            )
240            .into();
241        };
242
243        // "json_value_array": "[1,\"lol\"]"
244        let Ok(row) = serde_json::from_str::<Value>(json_value_array) else {
245            return ExtensionResponseEnum::Failure(
246                "Could not parse the json_value_array".to_string(),
247            )
248            .into();
249        };
250
251        match table.insert(auto_rowid, &row) {
252            InsertResult::Success(rowid) => SuccessWithId(rowid).into(),
253            InsertResult::Constraint => ExtensionResponseEnum::Constraint().into(),
254            InsertResult::Err(err) => ExtensionResponseEnum::Failure(err).into(),
255        }
256    }
257}
258
259pub enum InsertResult {
260    Success(u64),
261    Constraint,
262    Err(String),
263}
264
265pub enum UpdateResult {
266    Success,
267    Constraint,
268    Err(String),
269}
270
271pub enum DeleteResult {
272    Success,
273    Err(String),
274}
275
276pub trait Table: Send + Sync + 'static {
277    fn name(&self) -> String;
278    fn columns(&self) -> Vec<ColumnDef>;
279    fn generate(&self, req: crate::ExtensionPluginRequest) -> crate::ExtensionResponse;
280    fn update(&mut self, rowid: u64, row: &serde_json::Value) -> UpdateResult;
281    fn delete(&mut self, rowid: u64) -> DeleteResult;
282    fn insert(&mut self, auto_rowid: bool, row: &serde_json::value::Value) -> InsertResult;
283    fn shutdown(&self);
284}
285
286pub trait ReadOnlyTable: Send + Sync + 'static {
287    fn name(&self) -> String;
288    fn columns(&self) -> Vec<ColumnDef>;
289    fn generate(&self, req: crate::ExtensionPluginRequest) -> crate::ExtensionResponse;
290    fn shutdown(&self);
291}