osquery_rust_ng/plugin/config/
mod.rs

1use crate::_osquery::{ExtensionPluginResponse, ExtensionResponse, ExtensionStatus};
2use crate::plugin::{ExtensionResponseEnum, OsqueryPlugin, Registry};
3use std::collections::{BTreeMap, HashMap};
4use std::sync::Arc;
5
6/// Trait for implementing configuration plugins in osquery-rust.
7///
8/// Configuration plugins provide osquery with its configuration data,
9/// which can come from various sources like files, HTTP endpoints, or
10/// other custom sources.
11pub trait ConfigPlugin: Send + Sync + 'static {
12    /// The name of the configuration plugin
13    fn name(&self) -> String;
14
15    /// Generate configuration data.
16    ///
17    /// Returns a map of config source names to JSON-encoded configuration strings.
18    /// The map typically contains a "main" key with the primary configuration.
19    fn gen_config(&self) -> Result<HashMap<String, String>, String>;
20
21    /// Generate pack configuration.
22    ///
23    /// Called when pack content is not provided inline with the configuration.
24    /// The `name` parameter is the pack name, and `value` is any additional context.
25    fn gen_pack(&self, name: &str, _value: &str) -> Result<String, String> {
26        Err(format!("Pack '{name}' not found"))
27    }
28
29    /// Called when the plugin is shutting down.
30    fn shutdown(&self) {}
31}
32
33/// Wrapper that adapts ConfigPlugin to OsqueryPlugin
34#[derive(Clone)]
35pub struct ConfigPluginWrapper {
36    plugin: Arc<dyn ConfigPlugin>,
37}
38
39impl ConfigPluginWrapper {
40    pub fn new<C: ConfigPlugin>(plugin: C) -> Self {
41        Self {
42            plugin: Arc::new(plugin),
43        }
44    }
45}
46
47impl OsqueryPlugin for ConfigPluginWrapper {
48    fn name(&self) -> String {
49        self.plugin.name()
50    }
51
52    fn registry(&self) -> Registry {
53        Registry::Config
54    }
55
56    fn routes(&self) -> ExtensionPluginResponse {
57        // Config plugins don't expose routes like table plugins do
58        ExtensionPluginResponse::new()
59    }
60
61    fn ping(&self) -> ExtensionStatus {
62        ExtensionStatus::default()
63    }
64
65    fn handle_call(&self, request: crate::_osquery::ExtensionPluginRequest) -> ExtensionResponse {
66        // Config plugins handle two actions: genConfig and genPack
67        let action = request.get("action").map(|s| s.as_str()).unwrap_or("");
68
69        match action {
70            "genConfig" => {
71                match self.plugin.gen_config() {
72                    Ok(config_map) => {
73                        let mut response = ExtensionPluginResponse::new();
74                        let mut row = BTreeMap::new();
75
76                        // Convert the config map to the expected format
77                        for (key, value) in config_map {
78                            row.insert(key, value);
79                        }
80
81                        response.push(row);
82                        let status = ExtensionStatus::default();
83                        ExtensionResponse::new(status, response)
84                    }
85                    Err(e) => ExtensionResponseEnum::Failure(e).into(),
86                }
87            }
88            "genPack" => {
89                let name = request.get("name").cloned().unwrap_or_default();
90                let value = request.get("value").cloned().unwrap_or_default();
91
92                match self.plugin.gen_pack(&name, &value) {
93                    Ok(pack_content) => {
94                        let mut response = ExtensionPluginResponse::new();
95                        let mut row = BTreeMap::new();
96                        row.insert("pack".to_string(), pack_content);
97                        response.push(row);
98                        let status = ExtensionStatus::default();
99                        ExtensionResponse::new(status, response)
100                    }
101                    Err(e) => ExtensionResponseEnum::Failure(e).into(),
102                }
103            }
104            _ => ExtensionResponseEnum::Failure(format!("Unknown config plugin action: {action}"))
105                .into(),
106        }
107    }
108
109    fn shutdown(&self) {
110        self.plugin.shutdown();
111    }
112}