From 3988569a744f71a17fa2c3a9b68d86568c139a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasip=20Timurta=C5=9F?= Date: Mon, 23 Jun 2025 15:21:57 +0200 Subject: [PATCH 1/2] chore: Update dependencies and enhance solsec scan command functionality - Added new dependencies including `bstr`, `dbus`, `libdbus-sys`, `normpath`, and `opener` to Cargo.toml and Cargo.lock for improved functionality. - Updated the solsec scan command to include a `--no-open` flag, allowing users to prevent automatic opening of HTML reports in CI environments. - Enhanced README.md to document the new `--no-open` option and clarify the behavior of automatic browser opening based on the environment. These changes improve the tool's usability and ensure it meets the needs of both interactive and automated workflows. --- Cargo.lock | 54 +++++++++++++++++++++ Cargo.toml | 2 + README.md | 35 ++++++++++++-- src/cli.rs | 127 ++++++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 10 ++-- src/report.rs | 2 +- 6 files changed, 202 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df84f79..eccf2a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.18.1" @@ -450,6 +461,17 @@ dependencies = [ "syn", ] +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi 0.3.9", +] + [[package]] name = "derive_builder" version = "0.20.2" @@ -1168,6 +1190,16 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "libloading" version = "0.8.8" @@ -1354,6 +1386,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "num-modular" version = "0.6.1" @@ -1409,6 +1450,18 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +[[package]] +name = "opener" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0812e5e4df08da354c851a3376fead46db31c2214f849d3de356d774d057681" +dependencies = [ + "bstr", + "dbus", + "normpath", + "windows-sys 0.59.0", +] + [[package]] name = "openssl" version = "0.10.73" @@ -2040,6 +2093,7 @@ dependencies = [ "handlebars", "libloading", "log", + "opener", "pretty_assertions", "regex", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 9064489..933720b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,8 @@ reqwest = { version = "0.12", features = ["json"] } toml = "0.8" # Directory utilities dirs = "6.0" +# Cross-platform browser opening +opener = "0.7" [dev-dependencies] tempfile = "3.8" diff --git a/README.md b/README.md index 568555c..5002aac 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,9 @@ solsec scan ./my-program --html-only --output results.html # Generate multiple formats at once solsec scan ./my-program --format json,html,markdown,csv +# Don't open browser automatically (useful for CI/automation) +solsec scan ./my-program --no-open + # Run fuzz testing solsec fuzz ./my-solana-program --timeout 300 ``` @@ -62,6 +65,8 @@ solsec fuzz ./my-solana-program --timeout 300 Run static analysis on your Solana smart contracts. Generates both JSON and HTML If no path is provided, it recursively scans the current directory for all `.rs` files, automatically ignoring `target/` and `.git/` folders. +HTML reports automatically open in your default browser when running interactively, but remain closed in CI/automation environments. + ```bash solsec scan [PATH] [OPTIONS] @@ -71,25 +76,29 @@ OPTIONS: -f, --format Output formats (comma-separated) [default: json,html] [possible values: json, html, markdown, csv] --json-only Only generate JSON (perfect for CI/CD) --html-only Only generate HTML (perfect for humans) + --no-open Don't automatically open HTML report in browser --fail-on-critical Exit with non-zero code on critical issues [default: true] EXAMPLES: - # Scan the entire project (generates both JSON and HTML!) + # Scan the entire project (generates both JSON and HTML, opens in browser!) solsec scan # Scan a specific directory with default formats solsec scan ./programs/my-program - # Generate only JSON for CI/CD integration + # Generate only JSON for CI/CD integration (no browser opening) solsec scan ./programs --json-only --output results.json - # Generate only HTML for manual review + # Generate only HTML for manual review (opens in browser) solsec scan ./programs --html-only --output results.html + # Generate HTML but don't open browser (useful for automation) + solsec scan ./programs --html-only --no-open --output results.html + # Generate all available formats solsec scan ./programs --format json,html,markdown,csv - # Legacy: Scan with configuration file + # Scan with configuration file solsec scan ./programs --config solsec.toml --output ./security-results ``` @@ -299,6 +308,23 @@ rm -rf ./tmp-security-results echo "✅ Security scan passed!" ``` +## 🌐 Smart Browser Opening + +**Automatic browser opening** when generating HTML reports makes reviewing security findings effortless: + +**✅ Opens automatically when:** +- Running in an interactive terminal (not redirected) +- Generating HTML reports (`--html-only` or default formats) +- Not in CI/automation environments + +**❌ Stays closed when:** +- Running in CI environments (GitHub Actions, GitLab CI, etc.) +- Output is redirected or piped +- Using `--no-open` flag +- Only generating non-visual formats (JSON, CSV) + +This gives you the best of both worlds: **great UX for developers** and **automation-friendly behavior** for CI/CD. + ## 📊 Report Examples ### HTML Report @@ -307,6 +333,7 @@ Beautiful, interactive HTML reports with: - Detailed findings with code snippets - Actionable recommendations - Responsive design for all devices +- **Auto-opens in your browser for immediate review!** ### JSON Report Machine-readable format perfect for: diff --git a/src/cli.rs b/src/cli.rs index f8bd7c8..7e12243 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,6 +8,18 @@ use crate::fuzz::FuzzEngine; use crate::plugin::{PluginAction, PluginManager}; use crate::report::{ReportFormat, ReportGenerator}; +#[derive(Debug)] +pub struct ScanConfig { + pub path: PathBuf, + pub config: Option, + pub output: PathBuf, + pub formats: Vec, + pub json_only: bool, + pub html_only: bool, + pub no_open: bool, + pub fail_on_critical: bool, +} + #[derive(Parser)] #[command(name = "solsec")] #[command(about = "Solana Smart Contract Security Toolkit")] @@ -46,6 +58,10 @@ pub enum Commands { #[arg(long, conflicts_with = "format")] html_only: bool, + /// Don't automatically open HTML report in browser (opens by default in interactive mode) + #[arg(long)] + no_open: bool, + /// Fail with non-zero exit code on critical issues #[arg(long, default_value = "true")] fail_on_critical: bool, @@ -81,29 +97,28 @@ pub enum Commands { }, } -pub async fn handle_scan_command( - path: PathBuf, - config: Option, - output: PathBuf, - formats: Vec, - json_only: bool, - html_only: bool, - fail_on_critical: bool, -) -> Result<()> { - info!("Starting static analysis scan on: {}", path.display()); +pub async fn handle_scan_command(config: ScanConfig) -> Result<()> { + info!( + "Starting static analysis scan on: {}", + config.path.display() + ); - let mut analyzer = StaticAnalyzer::new(config)?; - let results = analyzer.analyze_path(&path).await?; + let mut analyzer = StaticAnalyzer::new(config.config)?; + let results = analyzer.analyze_path(&config.path).await?; // Determine which formats to generate - let formats_to_generate = if json_only { + let formats_to_generate = if config.json_only { vec![ReportFormat::Json] - } else if html_only { + } else if config.html_only { vec![ReportFormat::Html] } else { - formats + config.formats }; + // Check if we should open HTML before generating reports + let should_open = should_open_html(&formats_to_generate, config.no_open); + let mut html_file_path: Option = None; + // Generate reports in all requested formats let report_gen = ReportGenerator::new(); for format in formats_to_generate { @@ -114,14 +129,19 @@ pub async fn handle_scan_command( ReportFormat::Csv => "csv", }; - let output_file = if output.extension().is_some() { + let output_file = if config.output.extension().is_some() { // If user provided a specific filename, respect it for the first format - output.clone() + config.output.clone() } else { // Generate appropriate filename based on format - output.join(format!("security-report.{}", extension)) + config.output.join(format!("security-report.{}", extension)) }; + // Track HTML file path for opening later + if matches!(format, ReportFormat::Html) { + html_file_path = Some(output_file.clone()); + } + report_gen .generate_report(&results, &output_file, format.clone()) .await?; @@ -135,11 +155,18 @@ pub async fn handle_scan_command( critical_count, high_count ); - if fail_on_critical && critical_count > 0 { + if config.fail_on_critical && critical_count > 0 { error!("Critical issues found. Failing as requested."); std::process::exit(1); } + // Open HTML report in browser if appropriate + if should_open { + if let Some(html_path) = html_file_path { + open_html_file(&html_path)?; + } + } + Ok(()) } @@ -199,3 +226,65 @@ pub async fn handle_plugin_command(action: PluginAction, path: Option) Ok(()) } + +/// Detects if we're running in a CI environment +fn is_ci_environment() -> bool { + // Check common CI environment variables + std::env::var("CI").is_ok() + || std::env::var("GITHUB_ACTIONS").is_ok() + || std::env::var("GITLAB_CI").is_ok() + || std::env::var("JENKINS_URL").is_ok() + || std::env::var("TRAVIS").is_ok() + || std::env::var("CIRCLECI").is_ok() + || std::env::var("BUILDKITE").is_ok() + || std::env::var("TF_BUILD").is_ok() // Azure DevOps +} + +/// Detects if we're in an interactive terminal session +fn is_interactive() -> bool { + // Check if stdout is a terminal and not redirected + use std::io::IsTerminal; + std::io::stdout().is_terminal() +} + +/// Determines if we should automatically open the HTML report +fn should_open_html(formats: &[ReportFormat], no_open: bool) -> bool { + // Don't open if user explicitly disabled it + if no_open { + return false; + } + + // Don't open in CI environments + if is_ci_environment() { + return false; + } + + // Don't open if not in interactive terminal + if !is_interactive() { + return false; + } + + // Only open if HTML is being generated + formats.contains(&ReportFormat::Html) +} + +/// Opens the HTML file in the default browser +fn open_html_file(file_path: &PathBuf) -> Result<()> { + match opener::open(file_path) { + Ok(()) => { + info!( + "📖 Opening security report in browser: {}", + file_path.display() + ); + Ok(()) + } + Err(e) => { + warn!( + "Could not open HTML report in browser: {}. You can manually open: {}", + e, + file_path.display() + ); + Ok(()) // Don't fail the entire command if browser opening fails + } + } +} diff --git a/src/main.rs b/src/main.rs index 02a61b5..b13e14e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,18 +33,20 @@ async fn main() -> Result<()> { format, json_only, html_only, + no_open, fail_on_critical, } => { - cli::handle_scan_command( + let scan_config = cli::ScanConfig { path, config, output, - format, + formats: format, json_only, html_only, + no_open, fail_on_critical, - ) - .await + }; + cli::handle_scan_command(scan_config).await } Commands::Fuzz { path, diff --git a/src/report.rs b/src/report.rs index 4a04f1d..ecfa73a 100644 --- a/src/report.rs +++ b/src/report.rs @@ -11,7 +11,7 @@ use std::path::Path; use crate::analyzer::AnalysisResult; use crate::fuzz::FuzzResult; -#[derive(Debug, Clone, ValueEnum, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, ValueEnum, Serialize, Deserialize)] pub enum ReportFormat { Json, Html, From d600e8ea4378ecdd5d22726f487d4a659a9e7b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasip=20Timurta=C5=9F?= Date: Mon, 23 Jun 2025 15:24:02 +0200 Subject: [PATCH 2/2] docs: Update README.md for clarity and consistency in solsec scan command - Revised the README.md to enhance clarity in the usage instructions for the `solsec scan` command, including adjustments to the descriptions of output formats and browser opening behavior. - Removed redundant phrases and ensured consistent language throughout the documentation. These changes improve user understanding and streamline the documentation for the solsec tool. --- README.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 5002aac..d7ae3ac 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ A comprehensive security analysis tool for Solana smart contracts that helps dev - **Multiple Report Formats**: JSON, HTML, Markdown, and CSV outputs - **Plugin System**: Extensible architecture for custom security rules - **CI/CD Integration**: GitHub Actions support with automated security checks -- **Professional Reports**: Beautiful HTML reports with severity rankings and actionable recommendations +- **Professional Reports**: HTML reports with severity rankings and actionable recommendations - **Smart Error Handling**: Clear, colored error messages with proper path validation - **Comprehensive Examples**: 8 educational examples demonstrating vulnerabilities and secure patterns @@ -52,7 +52,7 @@ solsec scan ./my-program --html-only --output results.html # Generate multiple formats at once solsec scan ./my-program --format json,html,markdown,csv -# Don't open browser automatically (useful for CI/automation) +# Don't open browser automatically solsec scan ./my-program --no-open # Run fuzz testing @@ -63,9 +63,9 @@ solsec fuzz ./my-solana-program --timeout 300 ### `solsec scan` -Run static analysis on your Solana smart contracts. Generates both JSON and HTML If no path is provided, it recursively scans the current directory for all `.rs` files, automatically ignoring `target/` and `.git/` folders. +Run static analysis on your Solana smart contracts. Generates both JSON and HTML by default. If no path is provided, it recursively scans the current directory for all `.rs` files, automatically ignoring `target/` and `.git/` folders. -HTML reports automatically open in your default browser when running interactively, but remain closed in CI/automation environments. +HTML reports automatically open in the default browser when running interactively, but remain closed in CI/automation environments. ```bash solsec scan [PATH] [OPTIONS] @@ -74,25 +74,25 @@ OPTIONS: -c, --config Configuration file path -o, --output Output directory [default: ./solsec-results] -f, --format Output formats (comma-separated) [default: json,html] [possible values: json, html, markdown, csv] - --json-only Only generate JSON (perfect for CI/CD) - --html-only Only generate HTML (perfect for humans) + --json-only Only generate JSON + --html-only Only generate HTML --no-open Don't automatically open HTML report in browser --fail-on-critical Exit with non-zero code on critical issues [default: true] EXAMPLES: - # Scan the entire project (generates both JSON and HTML, opens in browser!) + # Scan the entire project (generates both JSON and HTML) solsec scan # Scan a specific directory with default formats solsec scan ./programs/my-program - # Generate only JSON for CI/CD integration (no browser opening) + # Generate only JSON for CI/CD integration solsec scan ./programs --json-only --output results.json - # Generate only HTML for manual review (opens in browser) + # Generate only HTML for manual review solsec scan ./programs --html-only --output results.html - # Generate HTML but don't open browser (useful for automation) + # Generate HTML but don't open browser solsec scan ./programs --html-only --no-open --output results.html # Generate all available formats @@ -308,35 +308,32 @@ rm -rf ./tmp-security-results echo "✅ Security scan passed!" ``` -## 🌐 Smart Browser Opening +## Browser Opening Behavior -**Automatic browser opening** when generating HTML reports makes reviewing security findings effortless: +HTML reports automatically open in the default browser under the following conditions: -**✅ Opens automatically when:** +**Opens automatically when:** - Running in an interactive terminal (not redirected) - Generating HTML reports (`--html-only` or default formats) - Not in CI/automation environments -**❌ Stays closed when:** +**Remains closed when:** - Running in CI environments (GitHub Actions, GitLab CI, etc.) - Output is redirected or piped - Using `--no-open` flag - Only generating non-visual formats (JSON, CSV) -This gives you the best of both worlds: **great UX for developers** and **automation-friendly behavior** for CI/CD. - ## 📊 Report Examples ### HTML Report -Beautiful, interactive HTML reports with: +Interactive HTML reports with: - Executive summary with issue counts by severity - Detailed findings with code snippets - Actionable recommendations - Responsive design for all devices -- **Auto-opens in your browser for immediate review!** ### JSON Report -Machine-readable format perfect for: +Machine-readable format for: - CI/CD pipeline integration - Custom tooling and analysis - Data processing and metrics @@ -395,7 +392,7 @@ solsec scan examples/unchecked_account/vulnerable.rs # 6 issues found solsec scan examples/reentrancy/vulnerable.rs # 2 issues found # Test secure examples (should find 0 issues) -solsec scan examples/*/secure.rs # All pass! +solsec scan examples/*/secure.rs # No issues found # Comprehensive analysis solsec scan examples/ # 26 total issues across all vulnerable examples