use regex::Regex;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum GuardAction {
Reject,
Warn,
RequireApproval,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GuardViolation {
pub guard_name: String,
pub violating_content: String,
pub message: String,
pub action: GuardAction,
}
pub trait Guard: Send + Sync {
fn name(&self) -> &str;
fn action(&self) -> GuardAction;
fn is_enabled(&self) -> bool;
fn check_path(&self, path: &Path) -> Option<GuardViolation>;
fn check_content(&self, content: &str, path: &Path) -> Option<GuardViolation>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathGuard {
pub name: String,
pub allowed_pattern: String,
#[serde(skip)]
pub pattern_regex: Option<Regex>,
pub action: GuardAction,
pub enabled: bool,
}
impl PathGuard {
pub fn new(name: impl Into<String>, allowed_pattern: impl Into<String>) -> Self {
let pattern = allowed_pattern.into();
let regex = Self::glob_to_regex(&pattern);
Self {
name: name.into(),
allowed_pattern: pattern,
pattern_regex: regex,
action: GuardAction::Reject,
enabled: true,
}
}
fn glob_to_regex(pattern: &str) -> Option<Regex> {
let regex_str = pattern
.replace("**", "DOUBLESTAR")
.replace('*', "[^/]*")
.replace("DOUBLESTAR", ".*")
.replace('?', "[^/]");
Regex::new(&format!("^{}$", regex_str)).ok()
}
pub fn with_action(mut self, action: GuardAction) -> Self {
self.action = action;
self
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
}
impl Guard for PathGuard {
fn name(&self) -> &str {
&self.name
}
fn action(&self) -> GuardAction {
self.action
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn check_path(&self, path: &Path) -> Option<GuardViolation> {
if !self.enabled {
return None;
}
let path_str = path.to_string_lossy();
let matches = if let Some(ref regex) = self.pattern_regex {
regex.is_match(&path_str)
} else {
path_str.starts_with(&self.allowed_pattern.replace("**", ""))
};
if matches {
None
} else {
Some(GuardViolation {
guard_name: self.name.clone(),
violating_content: path_str.to_string(),
message: format!(
"Path '{}' is not allowed. Must match pattern '{}'",
path_str, self.allowed_pattern
),
action: self.action,
})
}
}
fn check_content(&self, _content: &str, _path: &Path) -> Option<GuardViolation> {
None }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecretGuard {
pub name: String,
pub pattern: String,
#[serde(skip)]
pub pattern_regex: Option<Regex>,
pub action: GuardAction,
pub enabled: bool,
}
impl SecretGuard {
pub fn new(name: impl Into<String>) -> Self {
let pattern =
r#"(?i)(password|secret|api[_-]?key|token|private[_-]?key)\s*[:=]\s*["'][^"']+["']"#;
Self {
name: name.into(),
pattern: pattern.to_string(),
pattern_regex: Regex::new(pattern).ok(),
action: GuardAction::Reject,
enabled: true,
}
}
pub fn with_pattern(mut self, pattern: impl Into<String>) -> Self {
let p = pattern.into();
self.pattern_regex = Regex::new(&p).ok();
self.pattern = p;
self
}
pub fn with_action(mut self, action: GuardAction) -> Self {
self.action = action;
self
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
}
impl Guard for SecretGuard {
fn name(&self) -> &str {
&self.name
}
fn action(&self) -> GuardAction {
self.action
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn check_path(&self, path: &Path) -> Option<GuardViolation> {
if !self.enabled {
return None;
}
let path_str = path.to_string_lossy();
let sensitive_extensions = [".env", ".credentials", ".secret", ".pem", ".key"];
for ext in sensitive_extensions {
if path_str.ends_with(ext) {
return Some(GuardViolation {
guard_name: self.name.clone(),
violating_content: path_str.to_string(),
message: format!("Cannot emit sensitive file type: '{}'", ext),
action: self.action,
});
}
}
None
}
fn check_content(&self, content: &str, path: &Path) -> Option<GuardViolation> {
if !self.enabled {
return None;
}
if let Some(ref regex) = self.pattern_regex {
if let Some(m) = regex.find(content) {
return Some(GuardViolation {
guard_name: self.name.clone(),
violating_content: m.as_str().to_string(),
message: format!(
"Potential secret detected in '{}': {}",
path.display(),
m.as_str()
),
action: self.action,
});
}
}
None
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelDependencyGuard {
pub name: String,
pub pattern: String,
#[serde(skip)]
pub pattern_regex: Option<Regex>,
pub action: GuardAction,
pub enabled: bool,
}
impl ModelDependencyGuard {
pub fn new(name: impl Into<String>) -> Self {
let pattern = r"(?i)(tensorflow|onnxruntime|candle-core|tch-rs|pytorch|transformers|huggingface|llm-chain|burn|ort)";
Self {
name: name.into(),
pattern: pattern.to_string(),
pattern_regex: Regex::new(pattern).ok(),
action: GuardAction::Reject,
enabled: true,
}
}
}
impl Guard for ModelDependencyGuard {
fn name(&self) -> &str {
&self.name
}
fn action(&self) -> GuardAction {
self.action
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn check_path(&self, _path: &Path) -> Option<GuardViolation> {
None
}
fn check_content(&self, content: &str, path: &Path) -> Option<GuardViolation> {
if !self.enabled {
return None;
}
if let Some(ref regex) = self.pattern_regex {
if let Some(m) = regex.find(content) {
return Some(GuardViolation {
guard_name: self.name.clone(),
violating_content: m.as_str().to_string(),
message: format!(
"Runtime model-serving dependency detected in '{}': {}. Zero Dependency and Compile-Time AutoML doctrines require artifact-resident cognition via branchless deterministic const structures.",
path.display(),
m.as_str()
),
action: self.action,
});
}
}
None
}
}
pub struct GuardSet {
guards: Vec<Box<dyn Guard>>,
}
impl std::fmt::Debug for GuardSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GuardSet")
.field("guards_count", &self.guards.len())
.finish()
}
}
impl Default for GuardSet {
fn default() -> Self {
Self::new()
}
}
impl Clone for GuardSet {
fn clone(&self) -> Self {
Self::default_v26()
}
}
impl GuardSet {
pub fn new() -> Self {
Self { guards: Vec::new() }
}
pub fn add_guard(&mut self, guard: impl Guard + 'static) {
self.guards.push(Box::new(guard));
}
pub fn check_path(&self, path: &Path) -> Vec<GuardViolation> {
self.guards
.iter()
.filter_map(|g| g.check_path(path))
.collect()
}
pub fn check_content(&self, content: &str, path: &Path) -> Vec<GuardViolation> {
self.guards
.iter()
.filter_map(|g| g.check_content(content, path))
.collect()
}
pub fn check(&self, path: &Path, content: &str) -> Vec<GuardViolation> {
let mut violations = self.check_path(path);
violations.extend(self.check_content(content, path));
violations
}
pub fn default_v26() -> Self {
let mut set = Self::new();
set.add_guard(PathGuard::new("path-guard", "ontology/**"));
set.add_guard(SecretGuard::new("secret-guard"));
set.add_guard(ModelDependencyGuard::new("model-dependency-guard"));
set
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_guard_allowed() {
let guard = PathGuard::new("test", "ontology/**");
assert!(guard.check_path(Path::new("ontology/domain.ttl")).is_none());
}
#[test]
fn test_path_guard_rejected() {
let guard = PathGuard::new("test", "ontology/**");
let violation = guard.check_path(Path::new("src/core.ttl"));
assert!(violation.is_some());
assert_eq!(violation.unwrap().guard_name, "test");
}
#[test]
fn test_secret_guard_clean_content() {
let guard = SecretGuard::new("secret-guard");
let content = "fn main() { println!(\"hello\"); }";
assert!(guard.check_content(content, Path::new("test.rs")).is_none());
}
#[test]
fn test_secret_guard_detects_secret() {
let guard = SecretGuard::new("secret-guard");
let content = r#"let api_key = "sk-1234567890";"#;
let violation = guard.check_content(content, Path::new("config.rs"));
assert!(violation.is_some());
}
#[test]
fn test_secret_guard_rejects_env_file() {
let guard = SecretGuard::new("secret-guard");
let violation = guard.check_path(Path::new(".env"));
assert!(violation.is_some());
}
#[test]
fn test_model_dependency_guard_clean() {
let guard = ModelDependencyGuard::new("model");
let content = "const A: u32 = 42;";
assert!(guard.check_content(content, Path::new("main.rs")).is_none());
}
#[test]
fn test_model_dependency_guard_detects_tensorflow() {
let guard = ModelDependencyGuard::new("model");
let content = "import tensorflow as tf";
let violation = guard.check_content(content, Path::new("model.py"));
assert!(violation.is_some());
assert!(violation
.unwrap()
.message
.contains("Zero Dependency and Compile-Time AutoML doctrines"));
}
#[test]
fn test_guard_set() {
let mut set = GuardSet::new();
set.add_guard(PathGuard::new("path", "ontology/**"));
set.add_guard(SecretGuard::new("secret"));
let violations = set.check(
Path::new("ontology/domain.ttl"),
"@prefix ex: <http://example.org/> .",
);
assert!(violations.is_empty());
let violations = set.check(
Path::new("src/core.ttl"),
"@prefix ex: <http://example.org/> .",
);
assert_eq!(violations.len(), 1);
}
}