use std::path::Path;
use serde::{Deserialize, Serialize};
use crate::error::{Result, ShieldError};
use crate::rules::policy::Policy;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub policy: Policy,
#[serde(default)]
pub scan: ScanConfig,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ScanConfig {
#[serde(default)]
pub ignore_tests: bool,
}
impl Config {
pub fn load(path: &Path) -> Result<Self> {
if !path.exists() {
return Ok(Self::default());
}
let content = std::fs::read_to_string(path)?;
let config: Config = toml::from_str(&content)?;
config.validate()?;
Ok(config)
}
fn validate(&self) -> Result<()> {
for s in &self.policy.suppressions {
if s.reason.trim().is_empty() {
return Err(ShieldError::Config(format!(
"Suppression for fingerprint '{}' must have a non-empty reason",
s.fingerprint,
)));
}
}
Ok(())
}
#[cfg(test)]
pub fn validate_for_test(&self) -> Result<()> {
self.validate()
}
pub fn starter_toml() -> &'static str {
r#"# AgentShield configuration
# See https://github.com/limaronaldo/agentshield for documentation.
[policy]
# Minimum severity to fail the scan (info, low, medium, high, critical).
fail_on = "high"
# Rule IDs to ignore entirely.
# ignore_rules = ["SHIELD-008"]
# Per-rule severity overrides.
# [policy.overrides]
# "SHIELD-012" = "info"
# Suppress specific findings by fingerprint.
# Run `agentshield scan . --format json` to see fingerprints.
# [[policy.suppressions]]
# fingerprint = "abc123..."
# reason = "False positive: input is validated by middleware"
# expires = "2026-06-01"
# [scan]
# Skip test files (test/, tests/, __tests__/, *.test.ts, *.spec.ts, etc.).
# ignore_tests = false
"#
}
}