Skip to main content

forge_guard/core/
config.rs

1use serde::{Deserialize, Serialize};
2
3/// Global forge-guard configuration.
4#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5pub struct ForgeGuardConfig {
6    /// Deployment guard settings.
7    #[serde(default)]
8    pub deployment: DeploymentConfig,
9    /// Security engine settings.
10    #[serde(default)]
11    pub security: SecurityConfig,
12    /// Report generation settings.
13    #[serde(default)]
14    pub report: ReportConfig,
15    /// Caching settings.
16    #[serde(default)]
17    pub cache: CacheConfig,
18    /// Plugin settings.
19    #[serde(default)]
20    pub plugins: PluginConfig,
21    /// AI auditor settings.
22    #[serde(default)]
23    pub ai: AiConfig,
24    /// Webhook notification settings.
25    #[serde(default)]
26    pub notifications: NotificationConfig,
27    /// Historical trend tracking settings.
28    #[serde(default)]
29    pub history: HistoryConfig,
30}
31
32/// Historical trend tracking configuration.
33///
34/// ```toml
35/// [history]
36/// enabled = true                  # persist audit results (opt-in)
37/// db_path = "~/.forge-guard/history.db"  # optional override
38/// ```
39#[derive(Debug, Clone, Default, Serialize, Deserialize)]
40pub struct HistoryConfig {
41    /// Persist audit results for trend tracking. Opt-in — when false,
42    /// audits are never recorded unless `--enable-history` is passed.
43    #[serde(default)]
44    pub enabled: bool,
45    /// SQLite database path. Defaults to `~/.forge-guard/history.db`.
46    /// `~` is expanded to the user's home directory; relative paths are
47    /// resolved against the project root.
48    #[serde(default)]
49    pub db_path: Option<String>,
50}
51
52/// Deployment guard configuration.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct DeploymentConfig {
55    /// Minimum security score required to deploy (0-100).
56    #[serde(default = "default_min_score")]
57    pub min_score: u8,
58    /// Whether to block on high-severity findings.
59    #[serde(default = "default_true")]
60    pub block_on_high: bool,
61    /// Whether to block on medium-severity findings.
62    #[serde(default)]
63    pub block_on_medium: bool,
64    /// Whether to block on critical findings.
65    #[serde(default = "default_true")]
66    pub block_on_critical: bool,
67    /// Require fuzzing to pass before deployment.
68    #[serde(default = "default_true")]
69    pub require_fuzzing: bool,
70    /// Require invariant tests to pass before deployment.
71    #[serde(default = "default_true")]
72    pub require_invariants: bool,
73    /// Whether to run deployment simulation.
74    #[serde(default = "default_true")]
75    pub simulate_deployment: bool,
76    /// Require on-chain verification after deployment.
77    #[serde(default)]
78    pub require_verification: bool,
79    /// Auto-verify after successful deployment.
80    #[serde(default)]
81    pub auto_verify: bool,
82    /// Explorer API key (reads chain-specific env var when empty).
83    #[serde(default)]
84    pub explorer_api_key: Option<String>,
85}
86
87fn default_min_score() -> u8 {
88    70
89}
90fn default_true() -> bool {
91    true
92}
93
94impl Default for DeploymentConfig {
95    fn default() -> Self {
96        Self {
97            min_score: 70,
98            block_on_high: true,
99            block_on_medium: false,
100            block_on_critical: true,
101            require_fuzzing: true,
102            require_invariants: true,
103            simulate_deployment: true,
104            require_verification: false,
105            auto_verify: false,
106            explorer_api_key: None,
107        }
108    }
109}
110
111/// Security engine configuration.
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct SecurityConfig {
114    /// Enable HIGH severity checks.
115    #[serde(default = "default_true")]
116    pub enable_high: bool,
117    /// Enable MEDIUM severity checks.
118    #[serde(default = "default_true")]
119    pub enable_medium: bool,
120    /// Enable LOW severity checks.
121    #[serde(default = "default_true")]
122    pub enable_low: bool,
123    /// Enable INFORMATIONAL checks.
124    #[serde(default)]
125    pub enable_info: bool,
126    /// Enable exploit path analysis.
127    #[serde(default = "default_true")]
128    pub exploit_analysis: bool,
129    /// Enable gas analysis.
130    #[serde(default)]
131    pub gas_analysis: bool,
132    /// Enable bytecode analysis.
133    #[serde(default)]
134    pub bytecode_analysis: bool,
135    /// Maximum number of findings per check.
136    #[serde(default = "default_max_findings")]
137    pub max_findings_per_check: usize,
138    /// Custom check severities (check_name -> Severity override).
139    #[serde(default)]
140    pub severity_overrides: std::collections::HashMap<String, String>,
141    /// Check IDs to force-enable (e.g. from an audit template).
142    /// Empty = all checks enabled per severity gates.
143    #[serde(default)]
144    pub enabled_checks: Vec<String>,
145    /// Check IDs to force-disable (e.g. from an audit template).
146    #[serde(default)]
147    pub disabled_checks: Vec<String>,
148}
149
150fn default_max_findings() -> usize {
151    50
152}
153
154impl Default for SecurityConfig {
155    fn default() -> Self {
156        Self {
157            enable_high: true,
158            enable_medium: true,
159            enable_low: true,
160            enable_info: false,
161            exploit_analysis: true,
162            gas_analysis: false,
163            bytecode_analysis: false,
164            max_findings_per_check: 50,
165            severity_overrides: std::collections::HashMap::new(),
166            enabled_checks: Vec::new(),
167            disabled_checks: Vec::new(),
168        }
169    }
170}
171
172/// Report configuration.
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ReportConfig {
175    /// Include code snippets in reports.
176    #[serde(default = "default_true")]
177    pub include_snippets: bool,
178    /// Include exploit paths in reports.
179    #[serde(default = "default_true")]
180    pub include_exploit_paths: bool,
181    /// Include recommendations in reports.
182    #[serde(default = "default_true")]
183    pub include_recommendations: bool,
184    /// Output directory for report files.
185    #[serde(default = "default_report_dir")]
186    pub output_dir: String,
187    /// Generate summary only (no details).
188    #[serde(default)]
189    pub summary_only: bool,
190}
191
192fn default_report_dir() -> String {
193    "reports".into()
194}
195
196impl Default for ReportConfig {
197    fn default() -> Self {
198        Self {
199            include_snippets: true,
200            include_exploit_paths: true,
201            include_recommendations: true,
202            output_dir: default_report_dir(),
203            summary_only: false,
204        }
205    }
206}
207
208/// Cache configuration.
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct CacheConfig {
211    /// Enable caching.
212    #[serde(default = "default_true")]
213    pub enabled: bool,
214    /// Cache directory path.
215    #[serde(default = "default_cache_dir")]
216    pub directory: String,
217    /// Maximum cache size in MB.
218    #[serde(default = "default_cache_size")]
219    pub max_size_mb: u64,
220    /// Cache TTL in seconds.
221    #[serde(default = "default_cache_ttl")]
222    pub ttl_seconds: u64,
223}
224
225fn default_cache_dir() -> String {
226    ".forge-guard-cache".into()
227}
228fn default_cache_size() -> u64 {
229    500
230}
231fn default_cache_ttl() -> u64 {
232    3600
233}
234
235impl Default for CacheConfig {
236    fn default() -> Self {
237        Self {
238            enabled: true,
239            directory: default_cache_dir(),
240            max_size_mb: 500,
241            ttl_seconds: 3600,
242        }
243    }
244}
245
246/// AI auditor configuration.
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct AiConfig {
249    /// Provider type: "openai", "claude", "ollama".
250    #[serde(default = "default_ai_provider")]
251    pub provider: String,
252    /// Model identifier.
253    #[serde(default = "default_ai_model")]
254    pub model: String,
255    /// Sampling temperature.
256    #[serde(default = "default_ai_temperature")]
257    pub temperature: f64,
258    /// Maximum tokens per response.
259    #[serde(default = "default_ai_max_tokens")]
260    pub max_tokens: u32,
261    /// Minimum confidence for findings (0.0–1.0).
262    #[serde(default = "default_ai_min_confidence")]
263    pub min_confidence: f64,
264    /// Run full audit (security + gas + logic) when enabled.
265    #[serde(default)]
266    pub full_audit: bool,
267}
268
269fn default_ai_provider() -> String {
270    "openai".into()
271}
272fn default_ai_model() -> String {
273    "gpt-4".into()
274}
275fn default_ai_temperature() -> f64 {
276    0.1
277}
278fn default_ai_max_tokens() -> u32 {
279    4000
280}
281fn default_ai_min_confidence() -> f64 {
282    0.5
283}
284
285impl Default for AiConfig {
286    fn default() -> Self {
287        Self {
288            provider: default_ai_provider(),
289            model: default_ai_model(),
290            temperature: default_ai_temperature(),
291            max_tokens: default_ai_max_tokens(),
292            min_confidence: default_ai_min_confidence(),
293            full_audit: false,
294        }
295    }
296}
297
298/// Plugin configuration.
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct PluginConfig {
301    /// Directories to search for plugins.
302    #[serde(default = "default_plugin_dirs")]
303    pub directories: Vec<String>,
304    /// Plugins to enable (empty = all).
305    #[serde(default)]
306    pub enabled: Vec<String>,
307    /// Plugins to disable.
308    #[serde(default)]
309    pub disabled: Vec<String>,
310    /// Allow loading plugins from outside the project.
311    #[serde(default)]
312    pub allow_external: bool,
313}
314
315fn default_plugin_dirs() -> Vec<String> {
316    vec![".forge-guard/plugins".into()]
317}
318
319impl Default for PluginConfig {
320    fn default() -> Self {
321        Self {
322            directories: default_plugin_dirs(),
323            enabled: Vec::new(),
324            disabled: Vec::new(),
325            allow_external: false,
326        }
327    }
328}
329
330/// Webhook notification configuration.
331///
332/// ```toml
333/// [notifications.slack]
334/// webhook = "https://hooks.slack.com/services/T000/B000/XXXX"
335/// min_severity = "high"
336///
337/// [notifications.discord]
338/// webhook = "https://discord.com/api/webhooks/123/abc"
339/// min_severity = "critical"
340/// ```
341#[derive(Debug, Clone, Default, Serialize, Deserialize)]
342pub struct NotificationConfig {
343    /// Slack incoming webhook settings.
344    #[serde(default)]
345    pub slack: NotificationEndpoint,
346    /// Discord webhook settings.
347    #[serde(default)]
348    pub discord: NotificationEndpoint,
349}
350
351/// A single webhook endpoint configuration.
352#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct NotificationEndpoint {
354    /// Incoming webhook URL. Empty when notifications are disabled.
355    #[serde(default)]
356    pub webhook: Option<String>,
357    /// Minimum finding severity that triggers a notification:
358    /// `informational`, `low`, `medium`, `high`, or `critical`.
359    #[serde(default = "default_notify_min_severity")]
360    pub min_severity: String,
361}
362
363fn default_notify_min_severity() -> String {
364    "high".into()
365}
366
367impl Default for NotificationEndpoint {
368    fn default() -> Self {
369        Self {
370            webhook: None,
371            min_severity: default_notify_min_severity(),
372        }
373    }
374}