Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod utils {
pub mod generic;
pub mod api;
}
mod targets;

use std::str::FromStr;
use clap::{Parser, Subcommand, CommandFactory};
Expand Down Expand Up @@ -86,6 +87,18 @@ enum Commands {

#[arg(short, long, help = "Output the result to a file. you can use the out_format option to specify the format of the output file.")]
out_file: Option<String>,

#[arg(
long,
help = "Specify specific files, directories, glob patterns, or git selectors to scan. Accepts comma-separated values. Examples: 'src/,pyproject.toml', 'src/**/*.py', 'git:diff=origin/main...HEAD', 'git:staged', 'git:untracked', or '-' to read from stdin (newline-delimited). Use '-0' for NUL-delimited stdin."
)]
target: Option<String>,

#[arg(
long,
help = "The name of the Corgea project. Defaults to git repository name if found, otherwise to the current directory name."
)]
project_name: Option<String>,
},
/// Wait for the latest in progress scan
Wait {
Expand Down Expand Up @@ -238,7 +251,7 @@ fn main() {
}
}
}
Some(Commands::Scan { scanner , fail_on, fail, only_uncommitted, scan_type, policy, out_format, out_file }) => {
Some(Commands::Scan { scanner , fail_on, fail, only_uncommitted, scan_type, policy, out_format, out_file, target, project_name }) => {
verify_token_and_exit_when_fail(&corgea_config);
if let Some(level) = fail_on {
if *scanner != Scanner::Blast {
Expand Down Expand Up @@ -321,7 +334,7 @@ fn main() {
match scanner {
Scanner::Snyk => scan::run_snyk(&corgea_config),
Scanner::Semgrep => scan::run_semgrep(&corgea_config),
Scanner::Blast => scanners::blast::run(&corgea_config, fail_on.clone(), fail, only_uncommitted, scan_type.clone(), policy.clone(), out_format.clone(), out_file.clone())
Scanner::Blast => scanners::blast::run(&corgea_config, fail_on.clone(), fail, only_uncommitted, scan_type.clone(), policy.clone(), out_format.clone(), out_file.clone(), target.clone(), project_name.clone())
}
}
Some(Commands::Wait { scan_id }) => {
Expand Down
126 changes: 86 additions & 40 deletions src/scanners/blast.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::utils;
use crate::config::Config;
use crate::targets;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::error::Error;
Expand All @@ -19,7 +20,15 @@ pub fn run(
policy: Option<String>,
out_format: Option<String>,
out_file: Option<String>,
target: Option<String>,
project_name: Option<String>,
) {
// Validate that only_uncommitted and target are not used together
if *only_uncommitted && target.is_some() {
eprintln!("--only_uncommitted and --target cannot be used together.");
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message uses --only_uncommitted format, but based on typical CLI conventions, this should likely be --only-uncommitted (with hyphen). Verify the actual flag name and ensure consistency between the error message and the CLI argument definition.

Suggested change
eprintln!("--only_uncommitted and --target cannot be used together.");
eprintln!("--only-uncommitted and --target cannot be used together.");

Copilot uses AI. Check for mistakes.
std::process::exit(1);
}

if *only_uncommitted {
match utils::generic::is_git_repo("./") {
Ok(false) => {
Expand Down Expand Up @@ -48,7 +57,7 @@ pub fn run(
println!("\n\n");
let temp_dir = env::temp_dir().join(format!("corgea/tmp/{}", Uuid::new_v4()));
fs::create_dir_all(&temp_dir).expect("Failed to create temp directory");
let project_name = utils::generic::get_current_working_directory().unwrap_or("unknown".to_string());
let project_name = utils::generic::determine_project_name(project_name.as_deref());
let zip_path = format!("{}/{}.zip", temp_dir.display(), project_name);
let repo_info = match utils::generic::get_repo_info("./") {
Ok(info) => info,
Expand All @@ -73,61 +82,98 @@ pub fn run(
utils::terminal::show_loading_message("Packaging your project... ([T]s)", stop_signal_clone);
});

if *only_uncommitted {
match utils::generic::get_untracked_and_modified_files("./") {
Ok(files) => {
let files_to_zip: Vec<(std::path::PathBuf, std::path::PathBuf)> = files
.iter()
.map(|file| (std::path::PathBuf::from(file), std::path::PathBuf::from(file)))
.collect();
println!("\rFiles to be submitted for partial scan:\n");
for (index, (_, original)) in files_to_zip.iter().enumerate() {
println!("{}: {}", index + 1, original.display());
}
print!("\n\n");
if files_to_zip.is_empty() {
let target_str: Option<&str> = if *only_uncommitted {
Some("git:staged,git:modified,git:untracked")
} else {
target.as_deref()
};

if let Some(target_value) = target_str {
match targets::resolve_targets(target_value) {
Ok(result) => {
if result.files.is_empty() {
*stop_signal.lock().unwrap() = true;
let _ = packaging_thread.join();
print!("\r{}", utils::terminal::set_text_color("", utils::terminal::TerminalColor::Reset));
eprintln!(
"\n\nOops! It seems there are no scannable uncommitted changes in your project.\nYou may have uncommitted changes, but none match the types of files we can scan.\n\n"
);
eprintln!("\n\nError: target resolved to zero files.\n");
eprintln!("Target value: {}\n", target_value);
eprintln!("Segment results:");
for segment_result in &result.segments {
if let Some(ref error) = segment_result.error {
eprintln!(" {}: ERROR - {}", segment_result.segment, error);
} else {
eprintln!(" {}: {} matches", segment_result.segment, segment_result.matches);
}
}
eprintln!("\nPlease check your target specification and try again.\n");
std::process::exit(1);
}
match utils::generic::create_zip_from_list_of_files(files_to_zip, &zip_path, None) {
Ok(_) => {},
Err(e) => {
*stop_signal.lock().unwrap() = true;
print!("\r{}", utils::terminal::set_text_color("", utils::terminal::TerminalColor::Reset));
eprintln!(
"\n\nUh-oh! We couldn't package your project at '{}'.\nThis might be due to insufficient permissions, invalid file paths, or a file system error.\nPlease check the directory and try again.\nError details:\n{}\n\n",
zip_path, e
);
std::process::exit(1);

let file_count = result.files.len();
if *only_uncommitted {
println!("\rFiles to be submitted for partial scan:\n");
for (index, file) in result.files.iter().enumerate() {
if let Ok(relative) = file.strip_prefix(std::env::current_dir().unwrap_or_default()) {
println!("{}: {}", index + 1, relative.display());
} else {
println!("{}: {}", index + 1, file.display());
}
}
println!();
} else {
println!("Scanning {} files (target mode)", file_count);

let display_count = std::cmp::min(20, file_count);
for file in result.files.iter().take(display_count) {
if let Ok(relative) = file.strip_prefix(std::env::current_dir().unwrap_or_default()) {
println!(" {}", relative.display());
} else {
println!(" {}", file.display());
}
}
if file_count > display_count {
println!(" (+{} more)", file_count - display_count);
}
println!();
}
},
}
Err(e) => {
*stop_signal.lock().unwrap() = true;
let _ = packaging_thread.join();
print!("\r{}", utils::terminal::set_text_color("", utils::terminal::TerminalColor::Reset));
eprintln!(
"\n\nFailed to retrieve untracked and modified files.\nError details:\n{}\n\n",
e
);
eprintln!("\n\nError resolving targets: {}\n", e);
std::process::exit(1);
}
}
} else {
match utils::generic::create_zip_from_filtered_files(".", None, &zip_path) {
Ok(_) => { },
Err(e) => {
}

match utils::generic::create_zip_from_target(target_str, &zip_path, None) {
Ok(added_files) => {
if added_files.is_empty() {
*stop_signal.lock().unwrap() = true;
let _ = packaging_thread.join();
print!("\r{}", utils::terminal::set_text_color("", utils::terminal::TerminalColor::Reset));
eprintln!(
"\n\nUh-oh! We couldn't package your project at '{}'.\nThis might be due to insufficient permissions, invalid file paths, or a file system error.\nPlease check the directory and try again.\nError details:\n{}\n\n",
zip_path, e
);
if *only_uncommitted {
eprintln!(
"\n\nOops! It seems there are no scannable uncommitted changes in your project.\nYou may have uncommitted changes, but none match the types of files we can scan.\n\n"
);
} else {
eprintln!(
"\n\nOops! No valid files found to scan after filtering.\n\n"
);
}
std::process::exit(1);
}
},
Err(e) => {
*stop_signal.lock().unwrap() = true;
let _ = packaging_thread.join();
print!("\r{}", utils::terminal::set_text_color("", utils::terminal::TerminalColor::Reset));
eprintln!(
"\n\nUh-oh! We couldn't package your project at '{}'.\nThis might be due to insufficient permissions, invalid file paths, or a file system error.\nPlease check the directory and try again.\nError details:\n{}\n\n",
zip_path, e
);
std::process::exit(1);
}
}
*stop_signal.lock().unwrap() = true;
Expand Down
Loading