corgea-cli 1.8.7__tar.gz → 1.8.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/Cargo.lock +5 -6
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/Cargo.toml +1 -1
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/PKG-INFO +1 -1
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scan.rs +33 -26
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/utils/api.rs +158 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/.github/workflows/npm-publish.yml +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/.github/workflows/release-binaries.yml +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/.github/workflows/release.yml +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/.github/workflows/test.yml +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/.github/workflows/update_docs..yml +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/.gitignore +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/LICENSE +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/README.md +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/bin/corgea.js +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/build_release.sh +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/package.json +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/pyproject.toml +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/scripts/npm/bundle-binaries.js +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/skills/corgea/SKILL.md +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/authorize.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/cicd.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/config.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/inspect.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/list.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/log.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/main.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scanners/blast.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scanners/fortify.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scanners/parsers/checkmarx.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scanners/parsers/coverity.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scanners/parsers/mod.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scanners/parsers/sarif.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/scanners/parsers/semgrep.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/setup_hooks.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/targets.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/utils/generic.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/utils/terminal.rs +0 -0
- {corgea_cli-1.8.7 → corgea_cli-1.8.8}/src/wait.rs +0 -0
|
@@ -340,7 +340,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|
|
340
340
|
|
|
341
341
|
[[package]]
|
|
342
342
|
name = "corgea"
|
|
343
|
-
version = "1.8.
|
|
343
|
+
version = "1.8.8"
|
|
344
344
|
dependencies = [
|
|
345
345
|
"chrono",
|
|
346
346
|
"clap",
|
|
@@ -1321,15 +1321,14 @@ dependencies = [
|
|
|
1321
1321
|
|
|
1322
1322
|
[[package]]
|
|
1323
1323
|
name = "openssl"
|
|
1324
|
-
version = "0.10.
|
|
1324
|
+
version = "0.10.80"
|
|
1325
1325
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1326
|
-
checksum = "
|
|
1326
|
+
checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
|
|
1327
1327
|
dependencies = [
|
|
1328
1328
|
"bitflags",
|
|
1329
1329
|
"cfg-if",
|
|
1330
1330
|
"foreign-types",
|
|
1331
1331
|
"libc",
|
|
1332
|
-
"once_cell",
|
|
1333
1332
|
"openssl-macros",
|
|
1334
1333
|
"openssl-sys",
|
|
1335
1334
|
]
|
|
@@ -1362,9 +1361,9 @@ dependencies = [
|
|
|
1362
1361
|
|
|
1363
1362
|
[[package]]
|
|
1364
1363
|
name = "openssl-sys"
|
|
1365
|
-
version = "0.9.
|
|
1364
|
+
version = "0.9.116"
|
|
1366
1365
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1367
|
-
checksum = "
|
|
1366
|
+
checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4"
|
|
1368
1367
|
dependencies = [
|
|
1369
1368
|
"cc",
|
|
1370
1369
|
"libc",
|
|
@@ -186,14 +186,15 @@ pub fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input:
|
|
|
186
186
|
let mut success = false;
|
|
187
187
|
|
|
188
188
|
while attempts < 3 && !success {
|
|
189
|
-
let form = reqwest::blocking::multipart::Form::new()
|
|
190
|
-
.file("file", fp)
|
|
191
|
-
.expect("Failed to read file");
|
|
192
|
-
|
|
193
189
|
debug(&format!("POST: {}", src_upload_url));
|
|
194
|
-
let res =
|
|
195
|
-
|
|
196
|
-
|
|
190
|
+
let res = utils::api::retry_on_network_error("file upload", || {
|
|
191
|
+
let form = reqwest::blocking::multipart::Form::new()
|
|
192
|
+
.file("file", fp)
|
|
193
|
+
.expect("Failed to read file");
|
|
194
|
+
client.post(&src_upload_url)
|
|
195
|
+
.multipart(form)
|
|
196
|
+
.send()
|
|
197
|
+
});
|
|
197
198
|
|
|
198
199
|
match res {
|
|
199
200
|
Ok(response) => {
|
|
@@ -211,8 +212,9 @@ pub fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input:
|
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
Err(e) => {
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
upload_error_count += 1;
|
|
216
|
+
eprintln!("Failed to upload file {} after network retries: {}", path, e);
|
|
217
|
+
break;
|
|
216
218
|
}
|
|
217
219
|
}
|
|
218
220
|
}
|
|
@@ -258,12 +260,14 @@ pub fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input:
|
|
|
258
260
|
|
|
259
261
|
for (index, chunk) in input_bytes.chunks(chunk_size).enumerate() {
|
|
260
262
|
debug(&format!("POST: {} (chunk {}/{})", scan_upload_url, index + 1, total_chunks));
|
|
261
|
-
let response =
|
|
262
|
-
.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
263
|
+
let response = utils::api::retry_on_network_error("scan chunk upload", || {
|
|
264
|
+
client.post(&scan_upload_url)
|
|
265
|
+
.header(header::CONTENT_TYPE, "application/json")
|
|
266
|
+
.header("Upload-Offset", offset.to_string())
|
|
267
|
+
.header("Upload-Length", input_size.to_string())
|
|
268
|
+
.body(chunk.to_vec())
|
|
269
|
+
.send()
|
|
270
|
+
});
|
|
267
271
|
|
|
268
272
|
let should_break = match &response {
|
|
269
273
|
Ok(res) => {
|
|
@@ -308,10 +312,12 @@ pub fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input:
|
|
|
308
312
|
last_response.expect("Failed to upload scan.")
|
|
309
313
|
} else {
|
|
310
314
|
debug(&format!("POST: {}", scan_upload_url));
|
|
311
|
-
|
|
312
|
-
.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
+
utils::api::retry_on_network_error("scan upload", || {
|
|
316
|
+
client.post(&scan_upload_url)
|
|
317
|
+
.header(header::CONTENT_TYPE, "application/json")
|
|
318
|
+
.body(input.clone())
|
|
319
|
+
.send()
|
|
320
|
+
})
|
|
315
321
|
};
|
|
316
322
|
|
|
317
323
|
let mut sast_scan_id: Option<String> = None;
|
|
@@ -381,14 +387,15 @@ pub fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input:
|
|
|
381
387
|
|
|
382
388
|
if git_config_path.exists() {
|
|
383
389
|
debug("Uploading .git/config");
|
|
384
|
-
let form = reqwest::blocking::multipart::Form::new()
|
|
385
|
-
.file("file", git_config_path)
|
|
386
|
-
.expect("Failed to read file");
|
|
387
|
-
|
|
388
390
|
debug(&format!("POST: {}", git_config_upload_url));
|
|
389
|
-
let res =
|
|
390
|
-
|
|
391
|
-
|
|
391
|
+
let res = utils::api::retry_on_network_error("git config upload", || {
|
|
392
|
+
let form = reqwest::blocking::multipart::Form::new()
|
|
393
|
+
.file("file", git_config_path)
|
|
394
|
+
.expect("Failed to read file");
|
|
395
|
+
client.post(&git_config_upload_url)
|
|
396
|
+
.multipart(form)
|
|
397
|
+
.send()
|
|
398
|
+
});
|
|
392
399
|
|
|
393
400
|
match res {
|
|
394
401
|
Ok(response) => {
|
|
@@ -144,6 +144,34 @@ pub fn http_client() -> HttpClient {
|
|
|
144
144
|
HttpClient { inner: SHARED_CLIENT.clone() }
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
#[cfg(not(test))]
|
|
148
|
+
const RETRY_BACKOFF_SECS: &[u64] = &[1, 2, 4, 8, 16, 32];
|
|
149
|
+
|
|
150
|
+
#[cfg(test)]
|
|
151
|
+
const RETRY_BACKOFF_SECS: &[u64] = &[0, 0, 0, 0, 0, 0];
|
|
152
|
+
|
|
153
|
+
pub fn retry_on_network_error<F, T>(operation: &str, mut make_request: F) -> reqwest::Result<T>
|
|
154
|
+
where
|
|
155
|
+
F: FnMut() -> reqwest::Result<T>,
|
|
156
|
+
{
|
|
157
|
+
let mut attempt = 0usize;
|
|
158
|
+
loop {
|
|
159
|
+
match make_request() {
|
|
160
|
+
Ok(result) => return Ok(result),
|
|
161
|
+
Err(e) if (e.is_connect() || e.is_timeout()) && attempt < RETRY_BACKOFF_SECS.len() => {
|
|
162
|
+
let delay = RETRY_BACKOFF_SECS[attempt];
|
|
163
|
+
eprintln!(
|
|
164
|
+
"Network error during {}: {}. Retrying in {}s... ({}/{})",
|
|
165
|
+
operation, e, delay, attempt + 1, RETRY_BACKOFF_SECS.len()
|
|
166
|
+
);
|
|
167
|
+
std::thread::sleep(std::time::Duration::from_secs(delay));
|
|
168
|
+
attempt += 1;
|
|
169
|
+
}
|
|
170
|
+
Err(e) => return Err(e),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
147
175
|
fn check_for_warnings(headers: &HeaderMap, status: StatusCode) {
|
|
148
176
|
if let Some(warning) = headers.get("warning") {
|
|
149
177
|
let warnings = warning.to_str().unwrap().split(',');
|
|
@@ -913,3 +941,133 @@ pub struct SCAIssuesResponse {
|
|
|
913
941
|
pub total_pages: u32,
|
|
914
942
|
pub total_issues: u32,
|
|
915
943
|
}
|
|
944
|
+
|
|
945
|
+
#[cfg(test)]
|
|
946
|
+
mod tests {
|
|
947
|
+
use super::*;
|
|
948
|
+
use std::cell::Cell;
|
|
949
|
+
use std::net::TcpListener;
|
|
950
|
+
use std::thread;
|
|
951
|
+
use std::time::Duration;
|
|
952
|
+
|
|
953
|
+
fn connection_refused_error() -> reqwest::Error {
|
|
954
|
+
let listener = TcpListener::bind("127.0.0.1:0").expect("failed to bind ephemeral port");
|
|
955
|
+
let port = listener.local_addr().expect("failed to get listener addr").port();
|
|
956
|
+
drop(listener);
|
|
957
|
+
|
|
958
|
+
reqwest::blocking::Client::builder()
|
|
959
|
+
.connect_timeout(Duration::from_secs(1))
|
|
960
|
+
.build()
|
|
961
|
+
.expect("failed to build client")
|
|
962
|
+
.get(format!("http://127.0.0.1:{port}"))
|
|
963
|
+
.send()
|
|
964
|
+
.expect_err("expected connection error")
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
fn timeout_error() -> reqwest::Error {
|
|
968
|
+
let listener = TcpListener::bind("127.0.0.1:0").expect("failed to bind ephemeral port");
|
|
969
|
+
let port = listener.local_addr().expect("failed to get listener addr").port();
|
|
970
|
+
|
|
971
|
+
thread::spawn(move || {
|
|
972
|
+
if let Ok((_, _)) = listener.accept() {
|
|
973
|
+
thread::sleep(Duration::from_secs(30));
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
reqwest::blocking::Client::builder()
|
|
978
|
+
.timeout(Duration::from_millis(200))
|
|
979
|
+
.build()
|
|
980
|
+
.expect("failed to build client")
|
|
981
|
+
.get(format!("http://127.0.0.1:{port}"))
|
|
982
|
+
.send()
|
|
983
|
+
.expect_err("expected timeout error")
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
fn non_retryable_error() -> reqwest::Error {
|
|
987
|
+
let err = reqwest::blocking::Client::new()
|
|
988
|
+
.get("http://[::1:bad")
|
|
989
|
+
.send()
|
|
990
|
+
.expect_err("expected request error");
|
|
991
|
+
|
|
992
|
+
assert!(
|
|
993
|
+
!err.is_connect() && !err.is_timeout(),
|
|
994
|
+
"expected a non-retryable reqwest error, got: {err}"
|
|
995
|
+
);
|
|
996
|
+
err
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
#[test]
|
|
1000
|
+
fn retry_on_network_error_returns_ok_on_first_success() {
|
|
1001
|
+
let attempts = Cell::new(0);
|
|
1002
|
+
|
|
1003
|
+
let result = retry_on_network_error("test operation", || {
|
|
1004
|
+
attempts.set(attempts.get() + 1);
|
|
1005
|
+
Ok("success")
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
assert_eq!(result.unwrap(), "success");
|
|
1009
|
+
assert_eq!(attempts.get(), 1);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
#[test]
|
|
1013
|
+
fn retry_on_network_error_retries_connect_errors_then_succeeds() {
|
|
1014
|
+
let attempts = Cell::new(0);
|
|
1015
|
+
|
|
1016
|
+
let result = retry_on_network_error("test operation", || {
|
|
1017
|
+
let attempt = attempts.get() + 1;
|
|
1018
|
+
attempts.set(attempt);
|
|
1019
|
+
if attempt < 3 {
|
|
1020
|
+
Err(connection_refused_error())
|
|
1021
|
+
} else {
|
|
1022
|
+
Ok(42)
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
assert_eq!(result.unwrap(), 42);
|
|
1027
|
+
assert_eq!(attempts.get(), 3);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
#[test]
|
|
1031
|
+
fn retry_on_network_error_retries_timeout_errors() {
|
|
1032
|
+
let attempts = Cell::new(0);
|
|
1033
|
+
|
|
1034
|
+
let result = retry_on_network_error("test operation", || {
|
|
1035
|
+
let attempt = attempts.get() + 1;
|
|
1036
|
+
attempts.set(attempt);
|
|
1037
|
+
if attempt == 1 {
|
|
1038
|
+
Err(timeout_error())
|
|
1039
|
+
} else {
|
|
1040
|
+
Ok("recovered")
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
assert_eq!(result.unwrap(), "recovered");
|
|
1045
|
+
assert_eq!(attempts.get(), 2);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
#[test]
|
|
1049
|
+
fn retry_on_network_error_does_not_retry_non_network_errors() {
|
|
1050
|
+
let attempts = Cell::new(0);
|
|
1051
|
+
|
|
1052
|
+
let result: reqwest::Result<()> = retry_on_network_error("test operation", || {
|
|
1053
|
+
attempts.set(attempts.get() + 1);
|
|
1054
|
+
Err(non_retryable_error())
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
assert!(result.is_err());
|
|
1058
|
+
assert_eq!(attempts.get(), 1);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
#[test]
|
|
1062
|
+
fn retry_on_network_error_gives_up_after_max_retries() {
|
|
1063
|
+
let attempts = Cell::new(0);
|
|
1064
|
+
|
|
1065
|
+
let result: reqwest::Result<()> = retry_on_network_error("test operation", || {
|
|
1066
|
+
attempts.set(attempts.get() + 1);
|
|
1067
|
+
Err(connection_refused_error())
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
assert!(result.is_err());
|
|
1071
|
+
assert_eq!(attempts.get(), RETRY_BACKOFF_SECS.len() + 1);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|