agentshield/config/
mod.rs1use std::path::Path;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::{Result, ShieldError};
6use crate::rules::policy::Policy;
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub struct Config {
11 #[serde(default)]
12 pub policy: Policy,
13 #[serde(default)]
14 pub scan: ScanConfig,
15}
16
17#[derive(Debug, Clone, Default, Serialize, Deserialize)]
19pub struct ScanConfig {
20 #[serde(default)]
22 pub ignore_tests: bool,
23}
24
25impl Config {
26 pub fn load(path: &Path) -> Result<Self> {
28 if !path.exists() {
29 return Ok(Self::default());
30 }
31 let content = std::fs::read_to_string(path)?;
32 let config: Config = toml::from_str(&content)?;
33 config.validate()?;
34 Ok(config)
35 }
36
37 fn validate(&self) -> Result<()> {
42 for s in &self.policy.suppressions {
43 if s.reason.trim().is_empty() {
44 return Err(ShieldError::Config(format!(
45 "Suppression for fingerprint '{}' must have a non-empty reason",
46 s.fingerprint,
47 )));
48 }
49 }
50 Ok(())
51 }
52
53 #[cfg(test)]
55 pub fn validate_for_test(&self) -> Result<()> {
56 self.validate()
57 }
58
59 pub fn starter_toml() -> &'static str {
61 r#"# AgentShield configuration
62# See https://github.com/limaronaldo/agentshield for documentation.
63
64[policy]
65# Minimum severity to fail the scan (info, low, medium, high, critical).
66fail_on = "high"
67
68# Rule IDs to ignore entirely.
69# ignore_rules = ["SHIELD-008"]
70
71# Per-rule severity overrides.
72# [policy.overrides]
73# "SHIELD-012" = "info"
74
75# Suppress specific findings by fingerprint.
76# Run `agentshield scan . --format json` to see fingerprints.
77# [[policy.suppressions]]
78# fingerprint = "abc123..."
79# reason = "False positive: input is validated by middleware"
80# expires = "2026-06-01"
81
82# [scan]
83# Skip test files (test/, tests/, __tests__/, *.test.ts, *.spec.ts, etc.).
84# ignore_tests = false
85"#
86 }
87}