use anyhow::Result;
use cipherrun::Args;
use cipherrun::commands::CommandRouter;
use cipherrun::utils::PathExt;
use clap::Parser;
use tracing::{Level, info};
use tracing_subscriber::FmtSubscriber;
#[tokio::main]
async fn main() -> Result<()> {
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
let log_level = std::env::var("RUST_LOG")
.ok()
.and_then(|s| s.parse::<Level>().ok())
.unwrap_or(Level::INFO);
let subscriber = FmtSubscriber::builder().with_max_level(log_level).finish();
tracing::subscriber::set_global_default(subscriber).expect("Failed to set subscriber");
let mut args = Args::parse();
if let Err(e) = args.validate() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
if args.version {
println!("CipherRun v{}", env!("CARGO_PKG_VERSION"));
println!("Fast, Modular TLS/SSL Security Scanner");
println!("Copyright (C) 2025 Marc Rivero (@seifreed)");
println!("Licensed under GPL-3.0");
return Ok(());
}
if let Some(config_path) = &args.api_server.config_example {
use cipherrun::api::ApiConfig;
ApiConfig::create_example(config_path.to_str_anyhow()?)?;
println!(
"✓ Example API configuration saved to: {}",
config_path.display()
);
return Ok(());
}
if args.compliance.list_frameworks {
use cipherrun::compliance::FrameworkLoader;
println!("Available Compliance Frameworks:\n");
let frameworks = FrameworkLoader::list_builtin_frameworks();
for (id, description) in frameworks {
println!(" {} - {}", id, description);
}
println!("\nUsage: cipherrun --compliance <FRAMEWORK_ID> <TARGET>");
println!("Example: cipherrun --compliance pci-dss-v4 example.com:443");
return Ok(());
}
if let Some(config_path) = &args.database.config_example {
use cipherrun::db::DatabaseConfig;
DatabaseConfig::create_example_config(config_path.to_str_anyhow()?)?;
println!(
"✓ Example database configuration saved to: {}",
config_path.display()
);
return Ok(());
}
if args.output.no_colour || args.output.no_color {
args.output.color = 0;
}
if args.scan.show_ciphers {
use cipherrun::data::CIPHER_DB;
println!(
"CipherRun v{} - Supported Cipher Suites\n",
env!("CARGO_PKG_VERSION")
);
let all_ciphers = CIPHER_DB.get_all_ciphers();
println!("Total: {} cipher suites\n", all_ciphers.len());
for cipher in all_ciphers {
println!(
" 0x{} - {} / {}",
cipher.hexcode, cipher.openssl_name, cipher.iana_name
);
}
return Ok(());
}
if !args.output.quiet
&& !args.api_server.enable
&& !args.monitoring.enable
&& !args.monitoring.test_alert
&& !args.ct_logs.enable
&& args.compare.is_none()
&& args.changes.is_none()
&& args.trends.is_none()
&& args.dashboard.is_none()
&& if args.database.init
|| args.database.cleanup_days.is_some() || args.database.history.is_some() { args.target.is_some() || args.input_file.is_some() || args.mx_domain.is_some() } else { true }
{
display_banner();
}
if let Err(e) = CommandRouter::validate_routing(&args) {
eprintln!("Error: {}", e);
std::process::exit(1);
}
let command = CommandRouter::route(args)?;
info!("Executing command: {}", command.name());
command.execute().await?;
Ok(())
}
fn display_banner() {
println!(
r#"
╔═══════════════════════════════════════════════════════════╗
║ CipherRun v0.2.1 ║
║ Fast, Modular TLS/SSL Security Scanner (Rust) ║
║ ║
║ Author: Marc Rivero | @seifreed ║
╚═══════════════════════════════════════════════════════════╝
Licensed under GPL-3.0 | Use at your own risk
"#
);
}