From 6743d0f54bda9857e4ddad9641bf4eb5db778321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasip=20Timurta=C5=9F?= Date: Mon, 23 Jun 2025 14:56:06 +0200 Subject: [PATCH 1/5] docs: Update README.md and CLI for enhanced solsec scan functionality - Revised the README.md to reflect new features in the `solsec scan` command, including default JSON and HTML output, and options for generating specific formats (JSON, HTML, Markdown, CSV). - Updated CLI command definitions to support multiple output formats and added flags for generating only JSON or HTML outputs. - Removed the legacy report command from the CLI, streamlining the command structure. These changes improve user experience and clarify the capabilities of the solsec tool. --- .cursor/rules/rules.mdc | 5 +-- .github/workflows/ci.yml | 12 +----- README.md | 60 ++++++++++++++-------------- src/cli.rs | 84 +++++++++++++++++++++------------------- src/main.rs | 21 +++++++--- src/report.rs | 56 --------------------------- 6 files changed, 91 insertions(+), 147 deletions(-) diff --git a/.cursor/rules/rules.mdc b/.cursor/rules/rules.mdc index f78e311..5d1e5df 100644 --- a/.cursor/rules/rules.mdc +++ b/.cursor/rules/rules.mdc @@ -44,10 +44,9 @@ solana-smart-contract-security-toolkit/ ### CLI Commands -1. **`solsec scan`** - Static analysis of Solana programs +1. **`solsec scan`** - Static analysis of Solana programs (now generates JSON & HTML by default!) 2. **`solsec fuzz`** - Automated fuzz testing with IDL generation -3. **`solsec report`** - Generate reports in multiple formats (JSON, HTML, Markdown, CSV) -4. **`solsec plugin`** - Manage security rule plugins +3. **`solsec plugin`** - Manage security rule plugins ### Built-in Security Rules diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73b966c..80cf40f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,22 +85,14 @@ jobs: - name: Run security scan on self run: | - mkdir -p ./security-results - ./target/release/solsec scan ./src --output ./security-results/analysis.json --format json - continue-on-error: true - - - name: Generate security report - run: | - ./target/release/solsec report ./security-results --output ./security-report.html --format html + ./target/release/solsec scan ./src --output ./security-results continue-on-error: true - name: Upload security report uses: actions/upload-artifact@v4 with: name: security-report - path: | - ./security-results/ - ./security-report.html + path: ./security-results/ retention-days: 30 - name: Comment PR with security results diff --git a/README.md b/README.md index 0c17a5e..9f7f39c 100644 --- a/README.md +++ b/README.md @@ -32,24 +32,30 @@ cargo install --path . ### Basic Usage ```bash -# Scan the current project for security issues (recursively) +# Scan the current project (generates both JSON and HTML by default!) solsec scan # Scan a specific Solana program and set an output directory solsec scan ./my-solana-program --output ./results +# Generate only JSON (perfect for CI/CD) +solsec scan ./my-program --json-only --output results.json + +# Generate only HTML (perfect for human review) +solsec scan ./my-program --html-only --output results.html + +# Generate multiple formats at once +solsec scan ./my-program --format json,html,markdown,csv + # Run fuzz testing solsec fuzz ./my-solana-program --timeout 300 - -# Generate an HTML report -solsec report ./results --output report.html --format html ``` ## 📖 Commands ### `solsec scan` -Run static analysis on your Solana smart contracts. 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. **Now generates both JSON and HTML by default for the best UX!** If no path is provided, it recursively scans the current directory for all `.rs` files, automatically ignoring `target/` and `.git/` folders. ```bash solsec scan [PATH] [OPTIONS] @@ -57,21 +63,29 @@ solsec scan [PATH] [OPTIONS] OPTIONS: -c, --config Configuration file path -o, --output Output directory [default: ./solsec-results] - -f, --format Output format [default: json] [possible values: json, html, markdown, csv] + -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) --fail-on-critical Exit with non-zero code on critical issues [default: true] EXAMPLES: - # Scan the entire project recursively (default behavior) + # Scan the entire project (generates both JSON and HTML!) solsec scan - # Scan a specific directory + # Scan a specific directory with default formats solsec scan ./programs/my-program - # Scan with a configuration file and custom output directory - solsec scan ./programs --config solsec.toml --output ./security-results + # Generate only JSON for CI/CD integration + solsec scan ./programs --json-only --output results.json + + # Generate only HTML for manual review + solsec scan ./programs --html-only --output results.html - # Scan a single file and output as HTML - solsec scan ./src/main.rs --format html + # Generate all available formats + solsec scan ./programs --format json,html,markdown,csv + + # Legacy: Scan with configuration file + solsec scan ./programs --config solsec.toml --output ./security-results ``` ### `solsec fuzz` @@ -91,22 +105,7 @@ EXAMPLES: solsec fuzz ./programs --output ./custom-fuzz-results ``` -### `solsec report` -Generate human-readable reports from analysis results. - -```bash -solsec report [OPTIONS] - -OPTIONS: - -o, --output Output file path [default: ./report.html] - -f, --format Report format [default: html] [possible values: json, html, markdown, csv] - -EXAMPLES: - solsec report ./solsec-results - solsec report ./results --output security-report.md --format markdown - solsec report ./results --format csv > issues.csv -``` ### `solsec plugin` @@ -253,16 +252,13 @@ jobs: - name: Run security scan run: | - solsec scan ./programs --output ./security-results --format json - solsec report ./security-results --output ./security-report.html + solsec scan ./programs --output ./security-results - name: Upload security report uses: actions/upload-artifact@v3 with: name: security-report - path: | - ./security-results/ - ./security-report.html + path: ./security-results/ - name: Fail on critical issues run: | diff --git a/src/cli.rs b/src/cli.rs index 2f0635a..f8bd7c8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -34,9 +34,17 @@ pub enum Commands { #[arg(short, long, default_value = "./solsec-results")] output: PathBuf, - /// Output format - #[arg(short, long, default_value = "json")] - format: ReportFormat, + /// Output format(s) - can specify multiple: json,html,markdown,csv + #[arg(short, long, default_value = "json,html", value_delimiter = ',')] + format: Vec, + + /// Only generate JSON output (for CI/CD integration) + #[arg(long, conflicts_with = "format")] + json_only: bool, + + /// Only generate HTML output (for human review) + #[arg(long, conflicts_with = "format")] + html_only: bool, /// Fail with non-zero exit code on critical issues #[arg(long, default_value = "true")] @@ -62,21 +70,6 @@ pub enum Commands { output: PathBuf, }, - /// Generate reports from analysis results - Report { - /// Path to results directory - #[arg(value_name = "RESULTS")] - results: PathBuf, - - /// Output path for generated report - #[arg(short, long, default_value = "./report.html")] - output: PathBuf, - - /// Report format - #[arg(short, long, default_value = "html")] - format: ReportFormat, - }, - /// Manage security rule plugins Plugin { /// Plugin action @@ -92,7 +85,9 @@ pub async fn handle_scan_command( path: PathBuf, config: Option, output: PathBuf, - format: ReportFormat, + formats: Vec, + json_only: bool, + html_only: bool, fail_on_critical: bool, ) -> Result<()> { info!("Starting static analysis scan on: {}", path.display()); @@ -100,11 +95,37 @@ pub async fn handle_scan_command( let mut analyzer = StaticAnalyzer::new(config)?; let results = analyzer.analyze_path(&path).await?; - // Generate report + // Determine which formats to generate + let formats_to_generate = if json_only { + vec![ReportFormat::Json] + } else if html_only { + vec![ReportFormat::Html] + } else { + formats + }; + + // Generate reports in all requested formats let report_gen = ReportGenerator::new(); - report_gen - .generate_report(&results, &output, format) - .await?; + for format in formats_to_generate { + let extension = match format { + ReportFormat::Json => "json", + ReportFormat::Html => "html", + ReportFormat::Markdown => "md", + ReportFormat::Csv => "csv", + }; + + let output_file = if output.extension().is_some() { + // If user provided a specific filename, respect it for the first format + output.clone() + } else { + // Generate appropriate filename based on format + output.join(format!("security-report.{}", extension)) + }; + + report_gen + .generate_report(&results, &output_file, format.clone()) + .await?; + } let critical_count = results.iter().filter(|r| r.severity == "critical").count(); let high_count = results.iter().filter(|r| r.severity == "high").count(); @@ -145,23 +166,6 @@ pub async fn handle_fuzz_command( Ok(()) } -pub async fn handle_report_command( - results: PathBuf, - output: PathBuf, - format: ReportFormat, -) -> Result<()> { - info!("Generating report from: {}", results.display()); - - let report_gen = ReportGenerator::new(); - report_gen - .generate_from_directory(&results, &output, format) - .await?; - - info!("Report generated: {}", output.display()); - - Ok(()) -} - pub async fn handle_plugin_command(action: PluginAction, path: Option) -> Result<()> { let mut plugin_manager = PluginManager::new()?; diff --git a/src/main.rs b/src/main.rs index b731dbc..02a61b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,19 +31,28 @@ async fn main() -> Result<()> { config, output, format, + json_only, + html_only, fail_on_critical, - } => cli::handle_scan_command(path, config, output, format, fail_on_critical).await, + } => { + cli::handle_scan_command( + path, + config, + output, + format, + json_only, + html_only, + fail_on_critical, + ) + .await + } Commands::Fuzz { path, timeout, jobs, output, } => cli::handle_fuzz_command(path, timeout, jobs, output).await, - Commands::Report { - results, - output, - format, - } => cli::handle_report_command(results, output, format).await, + Commands::Plugin { action, path } => cli::handle_plugin_command(action, path).await, } } diff --git a/src/report.rs b/src/report.rs index 21a32fc..4a04f1d 100644 --- a/src/report.rs +++ b/src/report.rs @@ -90,62 +90,6 @@ impl ReportGenerator { self.write_report(&report, output_path, format).await } - pub async fn generate_from_directory( - &self, - results_dir: &Path, - output_path: &Path, - format: ReportFormat, - ) -> Result<()> { - info!( - "Generating report from directory: {}", - results_dir.display() - ); - - // Load analysis results - let mut analysis_results = Vec::new(); - let mut fuzz_results = None; - - if results_dir.exists() { - for entry in fs::read_dir(results_dir)? { - let entry = entry?; - let path = entry.path(); - - if path.extension().is_some_and(|ext| ext == "json") { - let filename = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); - - if filename.contains("analysis") || filename.contains("scan") { - let content = fs::read_to_string(&path)?; - - // Try to parse as SecurityReport first (from scan command) - if let Ok(security_report) = - serde_json::from_str::(&content) - { - analysis_results.extend(security_report.analysis_results); - } else { - // Fallback to parsing as Vec (legacy format) - let results: Vec = serde_json::from_str(&content) - .with_context(|| { - format!( - "Failed to parse analysis results from: {}", - path.display() - ) - })?; - analysis_results.extend(results); - } - } else if filename.contains("fuzz") { - let content = fs::read_to_string(&path)?; - fuzz_results = Some(serde_json::from_str(&content).with_context(|| { - format!("Failed to parse fuzz results from: {}", path.display()) - })?); - } - } - } - } - - let report = self.build_report(&analysis_results, fuzz_results).await?; - self.write_report(&report, output_path, format).await - } - async fn build_report( &self, analysis_results: &[AnalysisResult], From e17b70b760f35af2a414630090f7b19452de1047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasip=20Timurta=C5=9F?= Date: Mon, 23 Jun 2025 14:57:29 +0200 Subject: [PATCH 2/5] chore: Bump version to 0.1.6 in Cargo.toml, Cargo.lock, and UI package.json - Updated version number from 0.1.5 to 0.1.6 across Cargo.toml, Cargo.lock, and UI package.json. - Adjusted version reference in the application to ensure consistency in versioning throughout the project. These changes maintain versioning accuracy across the project. --- Cargo.lock | 2 +- Cargo.toml | 2 +- ui/package.json | 2 +- ui/src/App.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fc0973..df84f79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2029,7 +2029,7 @@ dependencies = [ [[package]] name = "solsec" -version = "0.1.5" +version = "0.1.6" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 8de91fa..9064489 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solsec" -version = "0.1.5" +version = "0.1.6" edition = "2021" description = "Solana Smart Contract Security Toolkit - Find security bugs before deployment" authors = ["Hasip Timurtas"] diff --git a/ui/package.json b/ui/package.json index 47ab4d3..a309761 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "solsec-ui", - "version": "0.1.5", + "version": "0.1.6", "private": true, "type": "module", "scripts": { diff --git a/ui/src/App.tsx b/ui/src/App.tsx index e5121db..0e7bf38 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -149,7 +149,7 @@ const App: React.FC = () => { const mockReport: SecurityReport = { metadata: { generated_at: new Date().toISOString(), - solsec_version: "0.1.5", + solsec_version: "0.1.6", total_files_scanned: 12, }, summary: { From c18c0f06856f59b53f4e08b57e81e8b1e1867e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasip=20Timurta=C5=9F?= Date: Mon, 23 Jun 2025 15:02:35 +0200 Subject: [PATCH 3/5] docs: Update README.md with badges for improved visibility - Added badges for version, downloads, license, and Rust edition to the README.md. - Enhanced the README to provide quick access to important project metrics and information. These updates improve the documentation and make key project details more accessible to users. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 9f7f39c..15e7ce1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # 🛡️ Solana Smart Contract Security Toolkit (solsec) +[![Crates.io](https://img.shields.io/crates/v/solsec.svg)](https://crates.io/crates/solsec) +[![Downloads](https://img.shields.io/crates/d/solsec.svg)](https://crates.io/crates/solsec) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Rust](https://img.shields.io/badge/rust-2021-orange.svg)](https://www.rust-lang.org) + A comprehensive security analysis tool for Solana smart contracts that helps developers identify vulnerabilities before deployment through static analysis and fuzz testing. ## ✨ Features From e5042254c78d7c8d3b985b35d267ffb9daebbafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasip=20Timurta=C5=9F?= Date: Mon, 23 Jun 2025 15:03:29 +0200 Subject: [PATCH 4/5] docs: Clarify README.md usage instructions for solsec scan command - Updated the README.md to enhance clarity in the usage instructions for the `solsec scan` command, specifying that it generates both JSON and HTML outputs. These changes improve the documentation and user understanding of the command's functionality. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15e7ce1..87d106e 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ cargo install --path . ### Basic Usage ```bash -# Scan the current project (generates both JSON and HTML by default!) +# Scan the current project and generates both JSON and HTML solsec scan # Scan a specific Solana program and set an output directory From 9dd3ce1e1eb197f5b193f1fe8ca6e938b9e72405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hasip=20Timurta=C5=9F?= Date: Mon, 23 Jun 2025 15:04:20 +0200 Subject: [PATCH 5/5] docs: Refine README.md for solsec scan command clarity - Updated the README.md to improve clarity in the usage instructions for the `solsec scan` command, specifically removing redundant phrases and enhancing readability. - Adjusted descriptions for JSON and HTML output options to streamline user understanding. These changes enhance the documentation and provide clearer guidance on the command's functionality. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 87d106e..568555c 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,10 @@ solsec scan # Scan a specific Solana program and set an output directory solsec scan ./my-solana-program --output ./results -# Generate only JSON (perfect for CI/CD) +# Generate only JSON solsec scan ./my-program --json-only --output results.json -# Generate only HTML (perfect for human review) +# Generate only HTML solsec scan ./my-program --html-only --output results.html # Generate multiple formats at once @@ -60,7 +60,7 @@ solsec fuzz ./my-solana-program --timeout 300 ### `solsec scan` -Run static analysis on your Solana smart contracts. **Now generates both JSON and HTML by default for the best UX!** 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 If no path is provided, it recursively scans the current directory for all `.rs` files, automatically ignoring `target/` and `.git/` folders. ```bash solsec scan [PATH] [OPTIONS]