diff --git a/src/config.rs b/src/config.rs index 91a5565..9ca1c27 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,7 +15,7 @@ //! ```toml //! hosts = ["example.com", "example.com:8443"] //! output = "summary" -//! exit_code = 1 +//! exit_code = true //! check_revocation = true //! //! [prometheus] @@ -37,8 +37,8 @@ pub struct Config { pub hosts: Option>, /// Output format: json, text, summary pub output: Option, - /// Exit code to use when certificates are expired/revoked - pub exit_code: Option, + /// Enable non-zero exit code when certificate issues are found + pub exit_code: Option, /// Enable certificate revocation checking pub check_revocation: Option, /// Prometheus configuration @@ -97,7 +97,7 @@ impl Config { /// /// - `hosts`: None (must be provided) /// - `output`: "summary" - /// - `exit_code`: 0 (don't fail on expired certificates) + /// - `exit_code`: false (don't fail on certificate issues) /// - `check_revocation`: false /// - `prometheus.enabled`: false /// - `prometheus.address`: "http://localhost:9091" @@ -109,7 +109,7 @@ impl Config { Config { hosts: None, output: Some("summary".to_string()), - exit_code: Some(0), + exit_code: Some(false), check_revocation: Some(false), prometheus: Some(PrometheusConfig { enabled: Some(false), @@ -185,7 +185,7 @@ impl Config { /// /// * `addresses` - List of hosts to check /// * `output` - Output format (json, text, summary) - /// * `exit_code` - Exit code for failures + /// * `exit_code` - Enable exit code on certificate issues /// * `prometheus` - Enable Prometheus metrics /// * `prometheus_address` - Prometheus push gateway address /// * `check_revocation` - Enable certificate revocation checking @@ -199,7 +199,7 @@ impl Config { pub fn from_cli_args( addresses: Option>, output: Option, - exit_code: Option, + exit_code: Option, prometheus: Option, prometheus_address: Option, check_revocation: Option, @@ -246,7 +246,7 @@ impl Config { "expired.badssl.com".to_string(), ]), output: Some("summary".to_string()), - exit_code: Some(1), + exit_code: Some(true), check_revocation: Some(true), prometheus: Some(PrometheusConfig { enabled: Some(true), @@ -295,7 +295,7 @@ mod tests { let toml_content = r#" hosts = ["jpbd.dev", "google.cl"] output = "json" - exit_code = 1 + exit_code = true check_revocation = true [prometheus] @@ -313,7 +313,7 @@ mod tests { Some(vec!["jpbd.dev".to_string(), "google.cl".to_string()]) ); assert_eq!(config.output, Some("json".to_string())); - assert_eq!(config.exit_code, Some(1)); + assert_eq!(config.exit_code, Some(true)); assert_eq!(config.check_revocation, Some(true)); let prometheus = config.prometheus.unwrap(); @@ -329,7 +329,7 @@ mod tests { let base_config = Config { hosts: Some(vec!["base.com".to_string()]), output: Some("text".to_string()), - exit_code: Some(0), + exit_code: Some(false), check_revocation: Some(false), prometheus: Some(PrometheusConfig { enabled: Some(false), @@ -342,7 +342,7 @@ mod tests { let override_config = Config { hosts: Some(vec!["override.com".to_string()]), output: None, - exit_code: Some(1), + exit_code: Some(true), check_revocation: Some(true), prometheus: Some(PrometheusConfig { enabled: Some(true), @@ -357,7 +357,7 @@ mod tests { // Override config should take precedence where specified assert_eq!(merged.hosts, Some(vec!["override.com".to_string()])); assert_eq!(merged.output, Some("text".to_string())); // From base (not overridden) - assert_eq!(merged.exit_code, Some(1)); // Overridden + assert_eq!(merged.exit_code, Some(true)); // Overridden assert_eq!(merged.check_revocation, Some(true)); // Overridden let prometheus = merged.prometheus.unwrap(); @@ -372,7 +372,7 @@ mod tests { assert_eq!(config.hosts, None); assert_eq!(config.output, Some("summary".to_string())); - assert_eq!(config.exit_code, Some(0)); + assert_eq!(config.exit_code, Some(false)); assert_eq!(config.check_revocation, Some(false)); let prometheus = config.prometheus.unwrap(); @@ -388,7 +388,7 @@ mod tests { let config = Config::from_cli_args( Some(vec!["cli.com".to_string()]), Some("json".to_string()), - Some(2), + Some(true), Some(true), Some("http://cli:9091".to_string()), Some(true), @@ -398,7 +398,7 @@ mod tests { assert_eq!(config.hosts, Some(vec!["cli.com".to_string()])); assert_eq!(config.output, Some("json".to_string())); - assert_eq!(config.exit_code, Some(2)); + assert_eq!(config.exit_code, Some(true)); assert_eq!(config.check_revocation, Some(true)); assert_eq!(config.grade, Some(true)); assert_eq!(config.min_validity, Some(45)); @@ -476,7 +476,7 @@ mod tests { let base = Config { hosts: Some(vec!["base.com".to_string()]), output: Some("json".to_string()), - exit_code: Some(1), + exit_code: Some(true), check_revocation: Some(true), prometheus: Some(PrometheusConfig { enabled: Some(true), @@ -497,7 +497,7 @@ mod tests { let merged = base.merge_with(empty_override); assert_eq!(merged.hosts, Some(vec!["base.com".to_string()])); assert_eq!(merged.output, Some("json".to_string())); - assert_eq!(merged.exit_code, Some(1)); + assert_eq!(merged.exit_code, Some(true)); assert_eq!(merged.check_revocation, Some(true)); assert_eq!(merged.grade, Some(true)); assert_eq!(merged.min_validity, Some(60)); diff --git a/src/main.rs b/src/main.rs index b4bc76f..a200ca9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,12 +40,13 @@ struct Args { #[arg(short, value_enum)] output: Option, - /// Exit with this code when expired or revoked certificates are found. + /// Exit with code 1 when certificate issues are found. /// - /// Defaults to 0 (always succeed). Set to 1 for CI/CD pipelines - /// that should fail on certificate issues. - #[arg(long)] - exit_code: Option, + /// Issues include: expired certificates, revoked certificates, + /// or certificates below the --min-validity threshold. + /// Disabled by default (always exits 0). + #[arg(long, default_value_t = false)] + exit_code: bool, /// Push certificate metrics to a Prometheus Push Gateway #[arg(long)] @@ -470,7 +471,7 @@ struct HostPort { struct FinalConfig { addresses: Vec, output: OutFormat, - exit_code: i32, + exit_code: bool, prometheus: bool, prometheus_address: String, check_revocation: bool, @@ -505,7 +506,7 @@ impl FinalConfig { Ok(FinalConfig { addresses, output, - exit_code: config.exit_code.unwrap_or(0), + exit_code: config.exit_code.unwrap_or(false), prometheus: prometheus_config.enabled.unwrap_or(false), prometheus_address: prometheus_config .address @@ -710,7 +711,7 @@ fn load_config(cli: &Args) -> Result { let cli_config = Config::from_cli_args( cli_addresses, cli.output.as_ref().map(|o| o.to_string()), - cli.exit_code, + if cli.exit_code { Some(true) } else { None }, cli.prometheus, cli.prometheus_address.clone(), cli.check_revocation, @@ -724,24 +725,25 @@ fn load_config(cli: &Args) -> Result { FinalConfig::from_merged_config(config) } -/// Exits the program with the appropriate exit code. +/// Exits the program with code 1 when certificate issues are detected. /// /// This function implements conditional exit behavior based on whether -/// any certificates failed validation (expired or revoked). +/// any certificates failed validation (expired, revoked, or below +/// the minimum validity threshold). /// /// # Arguments /// -/// * `exit_code` - The exit code to use when failures are detected +/// * `exit_code` - Whether to enable non-zero exit codes on failure /// * `failed_result` - Whether any certificate checks failed /// /// # Behavior /// -/// - If `failed_result` is `true` and `exit_code` is non-zero, exits with `exit_code` +/// - If `exit_code` is `true` and `failed_result` is `true`, exits with code 1 /// - Otherwise, exits normally with code 0 /// - Useful for CI/CD pipelines where exit code indicates build success/failure -fn exit(exit_code: i32, failed_result: bool) { - if exit_code != 0 && failed_result { - std::process::exit(exit_code) +fn exit(exit_code: bool, failed_result: bool) { + if exit_code && failed_result { + std::process::exit(1) } } @@ -939,7 +941,7 @@ mod tests { let config = Config { hosts: None, output: Some("summary".to_string()), - exit_code: Some(0), + exit_code: Some(false), check_revocation: Some(false), prometheus: None, grade: Some(false), @@ -954,7 +956,7 @@ mod tests { let config = Config { hosts: Some(vec![]), output: Some("summary".to_string()), - exit_code: Some(0), + exit_code: Some(false), check_revocation: Some(false), prometheus: None, grade: Some(false), @@ -969,7 +971,7 @@ mod tests { let config = Config { hosts: Some(vec!["example.com".to_string()]), output: Some("xml".to_string()), - exit_code: Some(0), + exit_code: Some(false), check_revocation: Some(false), prometheus: None, grade: Some(false), @@ -992,7 +994,7 @@ mod tests { }; let final_config = FinalConfig::from_merged_config(config).unwrap(); assert_eq!(final_config.output, OutFormat::Summary); - assert_eq!(final_config.exit_code, 0); + assert!(!final_config.exit_code); assert!(!final_config.check_revocation); assert!(!final_config.prometheus); assert!(!final_config.grade); @@ -1005,7 +1007,7 @@ mod tests { let config = Config { hosts: Some(vec!["a.com".to_string(), "b.com".to_string()]), output: Some("json".to_string()), - exit_code: Some(2), + exit_code: Some(true), check_revocation: Some(true), prometheus: Some(config::PrometheusConfig { enabled: Some(true), @@ -1017,7 +1019,7 @@ mod tests { let final_config = FinalConfig::from_merged_config(config).unwrap(); assert_eq!(final_config.addresses, vec!["a.com", "b.com"]); assert_eq!(final_config.output, OutFormat::Json); - assert_eq!(final_config.exit_code, 2); + assert!(final_config.exit_code); assert!(final_config.check_revocation); assert!(final_config.prometheus); assert_eq!(final_config.prometheus_address, "http://prom:9091");