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