osquery_rust_ng/plugin/table/
mod.rs1pub(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 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 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}