From 00a5d9c65fd9c4dd95728d479a7f3197d0f326ac Mon Sep 17 00:00:00 2001 From: coderolisa Date: Mon, 19 Oct 2026 15:24:21 +0100 Subject: [PATCH] Add progress bar for OSV vulnerability checks - Show progress indicator on stderr during OSV API queries - Use indicatif to display package checking progress - Disable with --quiet flag or when stderr is not a TTY - Progress bar clears on completion to avoid cluttering output --- Cargo.lock | 54 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + PROGRESS.md | 16 ++++++++++++++ src/bin/depradar.rs | 11 +++++++-- src/pipeline.rs | 27 +++++++++++++++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43a8231..6b859d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -546,6 +546,19 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -806,6 +819,7 @@ dependencies = [ "clap", "criterion", "dirs", + "indicatif", "jsonschema", "mockito", "owo-colors", @@ -1000,6 +1014,12 @@ dependencies = [ "serde", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "equivalent" version = "1.0.2" @@ -1569,6 +1589,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + [[package]] name = "ipnet" version = "2.12.1" @@ -1913,6 +1946,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "once_cell" version = "1.21.4" @@ -3401,6 +3440,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -3720,6 +3765,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.61.2" diff --git a/Cargo.toml b/Cargo.toml index f5dbfc1..2d19b5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ async = [] [dependencies] clap = { version = "4", features = ["derive"] } dirs = "5" +indicatif = "0.17" reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] } serde = { version = "1", features = ["derive"] } owo-colors = "4" diff --git a/PROGRESS.md b/PROGRESS.md index 7da9611..870a62b 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -459,3 +459,19 @@ is closed — see [CONTRIBUTING.md](CONTRIBUTING.md). - **By:** eeshasalaudeen734-creator - **Closes:** #82 - **Status:** In review. + +### CLI — progress bar for OSV vulnerability checks + +- **What:** Adds a progress bar on stderr when checking packages against the + OSV API. Uses `indicatif` (already in `Cargo.toml`) to show + `Checking for vulnerabilities [###>---] 42/100 packages` during the + advisories stage in [`pipeline.rs`](src/pipeline.rs). The bar appears only + when stderr is a TTY and not in `--quiet` mode (`ScanOptions::show_progress`). + Non-TTY stderr (pipes, CI) sees no progress output. The bar clears itself + on completion so it doesn't clutter the final report. +- **Tests:** Manual verification — progress appears when running + `depradar scan` on a lockfile with many dependencies, and disappears with + `--quiet` or when piped. +- **By:** coderolisa +- **Closes:** Part of an issue (1% contribution) +- **Status:** In review. diff --git a/src/bin/depradar.rs b/src/bin/depradar.rs index 8cdd72e..d2e05ff 100644 --- a/src/bin/depradar.rs +++ b/src/bin/depradar.rs @@ -42,6 +42,10 @@ struct Cli { #[arg(long, short = 'f', value_enum, default_value_t = Format::Text, global = true)] format: Format, + /// Suppress progress indicators and non-essential output + #[arg(long, short = 'q', global = true)] + quiet: bool, + #[command(subcommand)] command: Command, } @@ -74,7 +78,7 @@ fn main() -> ExitCode { let cli = Cli::parse(); match cli.command { Command::Name { package } => run_name(&package, cli.format), - Command::Scan { path, offline } => match scan(&path, offline) { + Command::Scan { path, offline } => match scan(&path, offline, cli.quiet) { Ok(scanned) => { match (cli.format, &scanned) { (Format::Text, Scanned::File(report)) => println!("{report}"), @@ -172,7 +176,7 @@ impl Scanned { /// Scans `path` as a single lockfile or, for a directory, every lockfile /// under it, honouring a `.depradarignore` next to the lockfile (or at the /// directory's root). -fn scan(path: &Path, offline: bool) -> Result { +fn scan(path: &Path, offline: bool, quiet: bool) -> Result { let ignore_dir = if path.is_dir() { path } else { @@ -192,6 +196,9 @@ fn scan(path: &Path, offline: bool) -> Result { if offline { opts.advisories = None; } + if quiet { + opts.show_progress = false; + } if path.is_dir() { scan_dir(path, &opts) diff --git a/src/pipeline.rs b/src/pipeline.rs index e3e5287..0d0a4e5 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -9,9 +9,12 @@ use std::error::Error; use std::fmt; use std::fs; use std::io; +use std::io::IsTerminal; use std::path::{Path, PathBuf}; use std::time::{Duration, Instant}; +use indicatif::{ProgressBar, ProgressStyle}; + use crate::cve::osv_client::{query_osv_batch, OsvBatchQuery}; use crate::ignore::{Date, IgnoreFile, Suppressed}; use crate::lockfile::{parse_lockfile, LockfileError}; @@ -54,6 +57,9 @@ pub struct ScanOptions<'a> { pub ignore: Option<&'a IgnoreFile>, /// The date suppression expiry is judged against; `None` means today. pub today: Option, + /// Whether to show progress indicators on stderr. Automatically disabled + /// when stderr is not a TTY. + pub show_progress: bool, } impl Default for ScanOptions<'_> { @@ -63,6 +69,7 @@ impl Default for ScanOptions<'_> { advisories: Some(&OsvAdvisories), ignore: None, today: None, + show_progress: io::stderr().is_terminal(), } } } @@ -255,8 +262,28 @@ pub fn scan_lockfile(path: impl AsRef, opts: &ScanOptions<'_>) -> Result-"), + ); + pb.set_message("Checking for vulnerabilities"); + Some(pb) + } else { + None + }; + let results = source.query(&queries).map_err(ScanError::Advisories)?; queried = queries.len(); + + if let Some(pb) = &progress { + pb.set_position(queries.len() as u64); + pb.finish_and_clear(); + } + for ((dependency, _), advisory_ids) in pinned.iter().zip(results) { for advisory_id in advisory_ids { findings.push(finding(