use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditConfig {
pub scoring_weights: ScoringWeights,
pub staleness_thresholds: StalenessThresholds,
pub license_policy: LicensePolicy,
pub footprint_thresholds: FootprintThresholds,
pub network: NetworkConfig,
pub ignored_dependencies: HashSet<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScoringWeights {
pub recency: f32,
pub maintenance: f32,
pub community: f32,
pub stability: f32,
pub security: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StalenessThresholds {
pub stale_days: u32,
pub risky_days: u32,
pub min_maintainers: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LicensePolicy {
pub allowed_licenses: HashSet<String>,
pub forbidden_licenses: HashSet<String>,
pub warn_on_copyleft: bool,
pub warn_on_unknown: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FootprintThresholds {
pub max_transitive_deps: Option<u32>,
pub max_footprint_risk: Option<f32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkConfig {
pub timeout_secs: u64,
pub max_retries: u32,
pub request_delay_ms: u64,
pub github_token: Option<String>,
pub gitlab_token: Option<String>,
pub enable_openssf: bool,
}
impl Default for AuditConfig {
fn default() -> Self {
Self {
scoring_weights: ScoringWeights::default(),
staleness_thresholds: StalenessThresholds::default(),
license_policy: LicensePolicy::default(),
footprint_thresholds: FootprintThresholds::default(),
network: NetworkConfig::default(),
ignored_dependencies: HashSet::new(),
}
}
}
impl Default for ScoringWeights {
fn default() -> Self {
Self {
recency: 0.35,
maintenance: 0.25,
community: 0.15,
stability: 0.10,
security: 0.15,
}
}
}
impl ScoringWeights {
pub fn validate(&self) -> Result<(), String> {
let sum = self.recency + self.maintenance + self.community + self.stability + self.security;
if (sum - 1.0).abs() > 0.01 {
return Err(format!(
"Scoring weights must sum to 1.0, got {}",
sum
));
}
Ok(())
}
pub fn normalize(&mut self) {
let sum = self.recency + self.maintenance + self.community + self.stability + self.security;
if sum > 0.0 {
self.recency /= sum;
self.maintenance /= sum;
self.community /= sum;
self.stability /= sum;
self.security /= sum;
}
}
}
impl Default for StalenessThresholds {
fn default() -> Self {
Self {
stale_days: 365, risky_days: 730, min_maintainers: 1,
}
}
}
impl Default for LicensePolicy {
fn default() -> Self {
Self {
allowed_licenses: HashSet::new(),
forbidden_licenses: HashSet::new(),
warn_on_copyleft: true,
warn_on_unknown: true,
}
}
}
impl Default for FootprintThresholds {
fn default() -> Self {
Self {
max_transitive_deps: Some(100),
max_footprint_risk: Some(0.8),
}
}
}
impl Default for NetworkConfig {
fn default() -> Self {
Self {
timeout_secs: 30,
max_retries: 3,
request_delay_ms: 100,
github_token: std::env::var("GITHUB_TOKEN").ok(),
gitlab_token: std::env::var("GITLAB_TOKEN").ok(),
enable_openssf: true,
}
}
}
impl NetworkConfig {
pub fn timeout(&self) -> Duration {
Duration::from_secs(self.timeout_secs)
}
pub fn request_delay(&self) -> Duration {
Duration::from_millis(self.request_delay_ms)
}
}
impl AuditConfig {
pub fn builder() -> AuditConfigBuilder {
AuditConfigBuilder::default()
}
}
#[derive(Default)]
pub struct AuditConfigBuilder {
scoring_weights: Option<ScoringWeights>,
staleness_thresholds: Option<StalenessThresholds>,
license_policy: Option<LicensePolicy>,
footprint_thresholds: Option<FootprintThresholds>,
network: Option<NetworkConfig>,
ignored_dependencies: HashSet<String>,
}
impl AuditConfigBuilder {
pub fn scoring_weights(mut self, weights: ScoringWeights) -> Self {
self.scoring_weights = Some(weights);
self
}
pub fn staleness_thresholds(mut self, thresholds: StalenessThresholds) -> Self {
self.staleness_thresholds = Some(thresholds);
self
}
pub fn license_policy(mut self, policy: LicensePolicy) -> Self {
self.license_policy = Some(policy);
self
}
pub fn footprint_thresholds(mut self, thresholds: FootprintThresholds) -> Self {
self.footprint_thresholds = Some(thresholds);
self
}
pub fn network(mut self, network: NetworkConfig) -> Self {
self.network = Some(network);
self
}
pub fn ignore_dependency(mut self, name: String) -> Self {
self.ignored_dependencies.insert(name);
self
}
pub fn build(self) -> AuditConfig {
AuditConfig {
scoring_weights: self.scoring_weights.unwrap_or_default(),
staleness_thresholds: self.staleness_thresholds.unwrap_or_default(),
license_policy: self.license_policy.unwrap_or_default(),
footprint_thresholds: self.footprint_thresholds.unwrap_or_default(),
network: self.network.unwrap_or_default(),
ignored_dependencies: self.ignored_dependencies,
}
}
}