Skip to content

Conversation

@Ibrahimrahhal
Copy link
Contributor

  • Added --project-name to allow users to specify a project name

    • Defaults to the Git repository name
    • Falls back to the directory name if Git metadata is unavailable
  • Added --target parameter for selective scanning, supporting:

    • File paths, directory paths, and glob patterns
    • Git-based selectors:
      • git:untracked
      • git:tracked
      • git:modified
      • git:diff=<range>
    • STDIN input:
      • Newline-delimited input
      • NUL-delimited input using -0
    • Comma-separated values for multiple targets

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds targeted scanning capabilities and project name customization to the Corgea CLI scanner. The key changes enable users to scan specific files or directories instead of always scanning the entire repository, and to specify custom project names.

Key changes:

  • Added --target parameter supporting files, directories, globs, git selectors (staged/modified/untracked/diff), and STDIN input
  • Added --project-name parameter with fallback logic (git repo name → directory name)
  • Refactored file packaging logic to support both full and targeted scans

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/utils/terminal.rs Added Yellow color variant to TerminalColor enum for warning messages
src/utils/generic.rs Refactored zip creation to support targeted scans; added project name determination and URL parsing utilities
src/targets.rs New module implementing target resolution logic for various input types (files, globs, git selectors, STDIN)
src/scanners/blast.rs Updated scan runner to accept target and project_name parameters; integrated targeted scanning workflow
src/main.rs Added CLI arguments for --target and --project-name parameters

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +230 to +248

let url = url.trim();

let url = url.strip_suffix(".git").unwrap_or(url);

if let Some(name) = url.split('/').last() {
let name = name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}

if let Some(name) = url.split(':').last() {
let name = name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}

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 function attempts to extract repository name by splitting on '/' first, then falls back to splitting on ':'. For SSH URLs like 'git@github.com:user/repo', splitting on '/' would incorrectly return 'repo' before checking the ':' delimiter. This works by accident but is fragile. The logic should check for SSH format first (contains ':' but not '://'), then handle HTTPS format (contains '://'), to be more robust.

Suggested change
let url = url.trim();
let url = url.strip_suffix(".git").unwrap_or(url);
if let Some(name) = url.split('/').last() {
let name = name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}
if let Some(name) = url.split(':').last() {
let name = name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}
//
// Prefer detecting SSH-style URLs (contain ':' but not '://')
// before generic '/' splitting, to avoid relying on fragile
// behaviour for different URL shapes.
let trimmed = url.trim();
let url_no_git = trimmed.strip_suffix(".git").unwrap_or(trimmed);
// HTTPS (or other scheme) URLs: scheme://host/path/to/repo
if url_no_git.contains("://") {
if let Some(after_scheme) = url_no_git.splitn(2, "://").nth(1) {
if let Some(name) = after_scheme.rsplit('/').next() {
let name = name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}
}
// SSH-style URLs: user@host:path/to/repo
} else if url_no_git.contains(':') {
if let Some(after_colon) = url_no_git.rsplitn(2, ':').next() {
if let Some(name) = after_colon.rsplit('/').next() {
let name = name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}
}
} else {
// Fallback: treat as a simple path and split on '/'
if let Some(name) = url_no_git.rsplit('/').next() {
let name = name.trim();
if !name.is_empty() {
return Some(name.to_string());
}
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +324 to +333
let parts: Vec<&str> = range.split("...").collect();
let (old_ref, new_ref) = if parts.len() == 2 {
(parts[0].trim(), parts[1].trim())
} else {
let parts: Vec<&str> = range.split("..").collect();
if parts.len() == 2 {
(parts[0].trim(), parts[1].trim())
} else {
return Err(format!("Invalid diff range format: {}. Expected format: 'old..new' or 'old...new'", range));
}
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 three-dot syntax check using split("...") will incorrectly match two-dot syntax. For example, "main..HEAD" will be split by "..." first, producing a single part "main..HEAD", then incorrectly fall through to the two-dot logic. Check for ".." first (which won't match "..."), then check for "..." to properly distinguish between the two git diff range syntaxes.

Suggested change
let parts: Vec<&str> = range.split("...").collect();
let (old_ref, new_ref) = if parts.len() == 2 {
(parts[0].trim(), parts[1].trim())
} else {
let parts: Vec<&str> = range.split("..").collect();
if parts.len() == 2 {
(parts[0].trim(), parts[1].trim())
} else {
return Err(format!("Invalid diff range format: {}. Expected format: 'old..new' or 'old...new'", range));
}
let (old_ref, new_ref) = if range.contains("..") && !range.contains("...") {
let parts: Vec<&str> = range.split("..").collect();
if parts.len() == 2 {
(parts[0].trim(), parts[1].trim())
} else {
return Err(format!("Invalid diff range format: {}. Expected format: 'old..new' or 'old...new'", range));
}
} else if range.contains("...") {
let parts: Vec<&str> = range.split("...").collect();
if parts.len() == 2 {
(parts[0].trim(), parts[1].trim())
} else {
return Err(format!("Invalid diff range format: {}. Expected format: 'old..new' or 'old...new'", range));
}
} else {
return Err(format!("Invalid diff range format: {}. Expected format: 'old..new' or 'old...new'", range));

Copilot uses AI. Check for mistakes.
}
match file.strip_prefix(&current_dir) {
Ok(relative) => Some((file.clone(), relative.to_path_buf())),
Err(_) => {
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.

When strip_prefix fails, the code uses the absolute path as both the full path and the "relative" path. This could lead to unexpected behavior downstream where relative paths are expected. Consider logging a warning or using a fallback that clearly indicates the path couldn't be made relative, rather than silently using the absolute path in both positions.

Suggested change
Err(_) => {
Err(_) => {
// Log a warning instead of silently treating the absolute path as "relative".
set_text_color(TerminalColor::Yellow);
eprintln!(
"Warning: failed to make path '{}' relative to '{}'; using absolute path as archive entry.",
file.display(),
current_dir.display()
);
set_text_color(TerminalColor::Reset);

Copilot uses AI. Check for mistakes.
) {
// 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.
Comment on lines +448 to +456
let abs_path = if path.is_absolute() {
path.to_path_buf()
} else {
std::env::current_dir()
.map_err(|e| format!("Failed to get current directory: {}", e))?
.join(path)
.canonicalize()
.map_err(|e| format!("Failed to canonicalize path: {}", e))?
};
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.

When the path is already absolute, it's not canonicalized, which could lead to inconsistent path representations (e.g., containing .. or . segments). Apply canonicalize to absolute paths as well to ensure all normalized paths have the same format.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants