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
76 changes: 62 additions & 14 deletions src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::utils;
use serde_json::json;
use std::path::Path;

#[allow(clippy::too_many_arguments)]
pub fn run(
config: &Config,
issues: &bool,
Expand All @@ -12,10 +13,13 @@ pub fn run(
page: &Option<u16>,
page_size: &Option<u16>,
scan_id: &Option<String>,
project_name_override: Option<String>,
repo_override: Option<String>,
) {
let project_name = utils::generic::determine_project_name(None);
println!();
if *sca_issues {
// SCA has no project parameter; this name is only error copy.
let project_name = utils::generic::determine_project_name(None);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new list --project-name / --repo flags are accepted with --sca-issues, but this branch discards both selectors and calls get_sca_issues with pagination/scan ID only. The SCA endpoint supports project and repo filters, so corgea list --sca-issues --project-name X currently succeeds while returning company-wide SCA findings instead of project X. Thread the explicit selector into get_sca_issues (skipping it only for the scan-ID route), or reject these flag combinations at clap parsing; add a request-target assertion proving the selected project/repo is sent.

let sca_issues_response = match utils::api::get_sca_issues(
&config.get_url(),
Some((*page).unwrap_or(1)),
Expand Down Expand Up @@ -108,6 +112,18 @@ pub fn run(
Some(sca_issues_response.total_pages),
);
} else if *issues {
// The --scan-id route hits /scan/{id}/issues and ignores the project.
let resolved = scan_id.is_none().then(|| {
utils::api::resolve_project_or_exit(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The resolved/user-supplied project name introduced here is passed to get_scan_issues, which still constructs src/utils/api.rs:479 with format!("...?project={}", project) and then appends &page=.... A valid explicit name such as foo&bar is therefore sent as project=foo&bar&page=1, so the server reads project foo and can return another project's issues. Build the /issues base URL first and pass project, page, and page_size via reqwest .query(...) (as query_scan_list already does), with a test asserting reserved characters are percent-encoded.

&config.get_url(),
project_name_override.as_deref(),
repo_override.as_deref(),
)
});
let project_name = resolved
.as_ref()
.map(|r| r.query_name.clone())
.unwrap_or_default();
let issues_response = match utils::api::get_scan_issues(
&config.get_url(),
&project_name,
Expand All @@ -119,10 +135,14 @@ pub fn run(
Err(e) => {
debug(&format!("Error Sending Request: {}", e));
if e.to_string().contains("404") {
if scan_id.is_some() {
log::error!("Scan with ID '{}' doesn't exist. Please run 'corgea scan' to create a new scan for this project.", scan_id.as_ref().unwrap());
} else {
log::error!("Project with name '{}' doesn't exist. Please run 'corgea scan' to create a new scan for this project.", project_name);
// `resolved` is None exactly on the --scan-id route.
match &resolved {
None => log::error!("Scan with ID '{}' doesn't exist. Please run 'corgea scan' to create a new scan for this project.", scan_id.as_ref().unwrap()),
Some(r) if r.confirmed => log::error!("Project '{}' has no issues yet. Run 'corgea scan' to create a scan for this project.", project_name),
Some(r) => log::error!(
"No Corgea project found for {}. Run 'corgea scan' to create one, or pass --project-name <NAME>.",
r.tried_label
),
}
} else {
log::error!(
Expand Down Expand Up @@ -259,26 +279,32 @@ pub fn run(

utils::terminal::print_table(table, issues_response.page, issues_response.total_pages);
} else {
let resolved = utils::api::resolve_project_or_exit(
&config.get_url(),
project_name_override.as_deref(),
repo_override.as_deref(),
);
let project_name = &resolved.query_name;
let (scans, page, total_pages) = match utils::api::query_scan_list(
&config.get_url(),
Some(&project_name),
Some(project_name),
*page,
*page_size,
) {
Ok(scans) => {
let page = scans.page;
let total_pages = scans.total_pages;
let filtered_scans: Vec<utils::api::ScanResponse> = scans
.scans
.unwrap_or_default()
.into_iter()
.filter(|scan| scan.project == project_name)
.collect();
(filtered_scans, page, total_pages)
// The server already filtered by the resolved project; the old
// client-side `scan.project == cwd_basename` pass would discard
// every repo-resolved scan. (COR-1577)
(scans.scans.unwrap_or_default(), page, total_pages)
}
Err(e) => {
if e.to_string().contains("404") {
log::error!("Project with name '{}' doesn't exist. Please run 'corgea scan' to create a new scan for this project.", project_name);
log::error!(
"No Corgea project found for {}. Run 'corgea scan' to create one, or pass --project-name <NAME>.",
resolved.tried_label
);
} else {
log::error!(
"Unable to fetch scans. Please check your connection and ensure that:\n\
Expand All @@ -296,7 +322,29 @@ pub fn run(
"total_pages": total_pages,
"results": scans
});
// The envelope prints first so JSON consumers get valid stdout even
// when the miss below exits 1.
println!("{}", serde_json::to_string_pretty(&output).unwrap());
}
// An unresolved project is a miss (exit 1, as --issues and `wait`); a
// confirmed project with no scans is a valid empty result. So is an
// explicit --project-name: /scans answers 200-empty either way, so the
// caller's own exact name is the better authority.
if scans.is_empty() && !resolved.confirmed && project_name_override.is_none() {
log::error!(
"No Corgea project found for {}. Run 'corgea scan' to create one, or pass --project-name <NAME>.",
resolved.tried_label
);
std::process::exit(1);
}
if *json {
return;
}
if scans.is_empty() {
println!(
"Project '{}' has no scans yet. Run 'corgea scan' to create one.",
project_name
);
return;
}
let mut table = vec![vec![
Expand Down
46 changes: 43 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,20 @@ enum Commands {
project_name: Option<String>,
},
/// Wait for the latest in progress scan
Wait { scan_id: Option<String> },
Wait {
scan_id: Option<String>,
#[arg(
long,
conflicts_with = "repo",
help = "Query this exact Corgea project name directly (skips repo auto-resolution)."
)]
project_name: Option<String>,
#[arg(
long,
help = "Resolve the project from this repo (org/repo slug or remote URL) instead of the git remote."
)]
repo: Option<String>,
},
/// List something, by default it lists the scans
#[command(alias = "ls")]
List {
Expand All @@ -166,6 +179,19 @@ enum Commands {

#[arg(long, value_parser = clap::value_parser!(u16), help = "Number of items per page")]
page_size: Option<u16>,

#[arg(
long,
conflicts_with = "repo",
help = "Query this exact Corgea project name directly (skips repo auto-resolution)."
)]
project_name: Option<String>,

#[arg(
long,
help = "Resolve the project from this repo (org/repo slug or remote URL) instead of the git remote."
)]
repo: Option<String>,
},
/// Inspect something, by default it will inspect a scan
Inspect {
Expand Down Expand Up @@ -663,9 +689,19 @@ fn main() {
),
}
}
Some(Commands::Wait { scan_id }) => {
Some(Commands::Wait {
scan_id,
project_name,
repo,
}) => {
verify_token_and_exit_when_fail(&corgea_config);
wait::run(&corgea_config, scan_id.clone(), None);
wait::run(
&corgea_config,
scan_id.clone(),
project_name.clone(),
repo.clone(),
None,
);
}
Some(Commands::List {
issues,
Expand All @@ -674,6 +710,8 @@ fn main() {
page_size,
scan_id,
sca_issues,
project_name,
repo,
}) => {
verify_token_and_exit_when_fail(&corgea_config);
if *issues && *sca_issues {
Expand All @@ -692,6 +730,8 @@ fn main() {
page,
page_size,
scan_id,
project_name.clone(),
repo.clone(),
);
}
Some(Commands::Inspect {
Expand Down
20 changes: 16 additions & 4 deletions src/scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,14 @@ pub fn run_semgrep(config: &Config, project_name: Option<String>) {

let output = run_command(&base_command.to_string(), command);

if let Some(result) = parse_scan(config, output, true, project_name) {
crate::wait::run(config, Some(result.scan_id), result.project_id);
if let Some(result) = parse_scan(config, output, true, project_name.clone()) {
crate::wait::run(
config,
Some(result.scan_id),
project_name,
None,
result.project_id,
);
}
}

Expand All @@ -80,8 +86,14 @@ pub fn run_snyk(config: &Config, project_name: Option<String>) {

let output = run_command(&base_command.to_string(), command);

if let Some(result) = parse_scan(config, output, true, project_name) {
crate::wait::run(config, Some(result.scan_id), result.project_id);
if let Some(result) = parse_scan(config, output, true, project_name.clone()) {
crate::wait::run(
config,
Some(result.scan_id),
project_name,
None,
result.project_id,
);
}
}

Expand Down
Loading
Loading