use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum FilterAction {
Block,
#[default]
Warn,
Log,
Sanitize,
Allow,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilterRule {
pub pattern: String,
pub is_regex: bool,
pub case_sensitive: bool,
pub action: FilterAction,
pub category: Option<String>,
pub description: Option<String>,
}
impl FilterRule {
pub fn keyword(pattern: impl Into<String>, action: FilterAction) -> Self {
Self {
pattern: pattern.into(),
is_regex: false,
case_sensitive: false,
action,
category: None,
description: None,
}
}
pub fn regex(pattern: impl Into<String>, action: FilterAction) -> Self {
Self {
pattern: pattern.into(),
is_regex: true,
case_sensitive: true,
action,
category: None,
description: None,
}
}
pub fn case_sensitive(mut self, sensitive: bool) -> Self {
self.case_sensitive = sensitive;
self
}
pub fn with_category(mut self, category: impl Into<String>) -> Self {
self.category = Some(category.into());
self
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GuardrailsConfig {
pub filter_input: bool,
pub filter_output: bool,
pub keyword_rules: Vec<FilterRule>,
pub pattern_rules: Vec<FilterRule>,
pub enable_pii_detection: bool,
pub check_pii_input: bool,
pub check_pii_output: bool,
pub sanitize_replacement: String,
pub pii_replacement: String,
pub stop_on_first_block: bool,
}
impl GuardrailsConfig {
pub fn builder() -> GuardrailsConfigBuilder {
GuardrailsConfigBuilder::default()
}
pub fn permissive() -> Self {
Self {
filter_input: false,
filter_output: false,
keyword_rules: Vec::new(),
pattern_rules: Vec::new(),
enable_pii_detection: false,
check_pii_input: false,
check_pii_output: false,
sanitize_replacement: "[FILTERED]".to_string(),
pii_replacement: "[PII]".to_string(),
stop_on_first_block: false,
}
}
pub fn strict() -> Self {
let mut config = Self::permissive();
config.filter_input = true;
config.filter_output = true;
config.enable_pii_detection = true;
config.check_pii_input = true;
config.check_pii_output = true;
config.stop_on_first_block = true;
config.keyword_rules = vec![
FilterRule::keyword("password", FilterAction::Warn)
.with_category("credentials")
.with_description("Password mention"),
FilterRule::keyword("api_key", FilterAction::Warn)
.with_category("credentials")
.with_description("API key mention"),
FilterRule::keyword("secret_key", FilterAction::Warn)
.with_category("credentials")
.with_description("Secret key mention"),
FilterRule::keyword("access_token", FilterAction::Warn)
.with_category("credentials")
.with_description("Access token mention"),
];
config
}
}
impl Default for GuardrailsConfig {
fn default() -> Self {
Self::permissive()
}
}
#[derive(Debug, Default)]
pub struct GuardrailsConfigBuilder {
filter_input: bool,
filter_output: bool,
keyword_rules: Vec<FilterRule>,
pattern_rules: Vec<FilterRule>,
enable_pii_detection: bool,
check_pii_input: bool,
check_pii_output: bool,
sanitize_replacement: Option<String>,
pii_replacement: Option<String>,
stop_on_first_block: bool,
}
impl GuardrailsConfigBuilder {
pub fn filter_input(mut self, enable: bool) -> Self {
self.filter_input = enable;
self
}
pub fn filter_output(mut self, enable: bool) -> Self {
self.filter_output = enable;
self
}
pub fn add_keyword_filter(mut self, keyword: impl Into<String>, action: FilterAction) -> Self {
self.filter_input = true; self.keyword_rules
.push(FilterRule::keyword(keyword, action));
self
}
pub fn add_pattern_filter(mut self, pattern: impl Into<String>, action: FilterAction) -> Self {
self.filter_input = true; self.pattern_rules.push(FilterRule::regex(pattern, action));
self
}
pub fn add_rule(mut self, rule: FilterRule) -> Self {
self.filter_input = true;
if rule.is_regex {
self.pattern_rules.push(rule);
} else {
self.keyword_rules.push(rule);
}
self
}
pub fn enable_pii_detection(mut self, enable: bool) -> Self {
self.enable_pii_detection = enable;
self.check_pii_input = enable;
self.check_pii_output = enable;
self
}
pub fn sanitize_replacement(mut self, replacement: String) -> Self {
self.sanitize_replacement = Some(replacement);
self
}
pub fn pii_replacement(mut self, replacement: String) -> Self {
self.pii_replacement = Some(replacement);
self
}
pub fn stop_on_first_block(mut self, stop: bool) -> Self {
self.stop_on_first_block = stop;
self
}
pub fn build(self) -> GuardrailsConfig {
GuardrailsConfig {
filter_input: self.filter_input,
filter_output: self.filter_output,
keyword_rules: self.keyword_rules,
pattern_rules: self.pattern_rules,
enable_pii_detection: self.enable_pii_detection,
check_pii_input: self.check_pii_input,
check_pii_output: self.check_pii_output,
sanitize_replacement: self
.sanitize_replacement
.unwrap_or_else(|| "[FILTERED]".to_string()),
pii_replacement: self.pii_replacement.unwrap_or_else(|| "[PII]".to_string()),
stop_on_first_block: self.stop_on_first_block,
}
}
}