Skip to main content

objectiveai_cli/filesystem/plugins/
whitelist.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4/// One whitelist row. Each field is a regex matched **anchored**
5/// against the corresponding install parameter (compiled as
6/// `^(?:pattern)$`). All four fields must match for the row to
7/// allow the install.
8///
9/// The host loads a list of these and accepts a plugin install only
10/// if the (owner, repository, commit_sha_or_HEAD, manifest.version)
11/// quadruple matches at least one row. Use `.*` for catch-all fields.
12#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
13#[schemars(rename = "filesystem.plugins.WhitelistEntry")]
14pub struct WhitelistEntry {
15    /// Regex matching the GitHub owner (org or user). Case-sensitive.
16    pub owner: String,
17    /// Regex matching the repository name.
18    pub repository: String,
19    /// Regex matching the commit sha. When the user omits
20    /// `--commit-sha`, this is matched against the literal string
21    /// `"HEAD"`.
22    pub commit_sha: String,
23    /// Regex matching the manifest's `version` field.
24    pub version: String,
25}
26
27/// The hard-coded default whitelist. Single entry: any repo under
28/// the ObjectiveAI GitHub org passes; everything else requires
29/// `--insecure`.
30pub fn default_whitelist() -> Vec<WhitelistEntry> {
31    vec![WhitelistEntry {
32        // (?i) is the regex inline flag for case-insensitive matching;
33        // GitHub usernames are case-insensitive at the URL level, so
34        // accept any capitalization of the canonical org name.
35        owner: "(?i)ObjectiveAI".to_string(),
36        repository: ".*".to_string(),
37        commit_sha: ".*".to_string(),
38        version: ".*".to_string(),
39    }]
40}
41
42/// Returns `Ok(true)` iff the quadruple matches at least one entry
43/// in `whitelist`. Each field is compiled as `^(?:pattern)$` so
44/// patterns like `Object` won't match `ObjectAttacker`. Returns
45/// `Err(regex::Error)` if any pattern fails to compile.
46pub fn check_plugin_whitelist(
47    owner: &str,
48    repository: &str,
49    commit_sha: &str,
50    version: &str,
51    whitelist: &[WhitelistEntry],
52) -> Result<bool, regex::Error> {
53    for entry in whitelist {
54        if anchored_match(&entry.owner, owner)?
55            && anchored_match(&entry.repository, repository)?
56            && anchored_match(&entry.commit_sha, commit_sha)?
57            && anchored_match(&entry.version, version)?
58        {
59            return Ok(true);
60        }
61    }
62    Ok(false)
63}
64
65fn anchored_match(pattern: &str, value: &str) -> Result<bool, regex::Error> {
66    let anchored = format!("^(?:{pattern})$");
67    let re = regex::Regex::new(&anchored)?;
68    Ok(re.is_match(value))
69}