corgea-cli 1.3.0__tar.gz → 1.3.2__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.
@@ -195,7 +195,7 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
195
195
 
196
196
  [[package]]
197
197
  name = "corgea"
198
- version = "1.3.0"
198
+ version = "1.3.2"
199
199
  dependencies = [
200
200
  "clap",
201
201
  "dirs",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "corgea"
3
- version = "1.3.0"
3
+ version = "1.3.2"
4
4
  edition = "2021"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: corgea-cli
3
- Version: 1.3.0
3
+ Version: 1.3.2
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -36,3 +36,19 @@ Corgea one-line command to upload SAST results. This command will run your scann
36
36
 
37
37
  </Card>
38
38
 
39
+ ## Installation
40
+
41
+ ### Using pip
42
+ ```
43
+ pip install corgea-cli
44
+ ```
45
+
46
+ ### Manual Installation
47
+ You can get the latest binaries for your OS from https://github.com/Corgea/cli/releases.
48
+
49
+ ### Setup
50
+ Once the binary is installed, login with your token from the Corgea app.
51
+ ```
52
+ corgea login <token>
53
+ ```
54
+
@@ -9,3 +9,19 @@ Corgea one-line command to upload SAST results. This command will run your scann
9
9
  [![](https://cdn.loom.com/sessions/thumbnails/0d3ed94d1f01401a86906fc9713ee709-with-play.gif)](https://www.loom.com/share/0d3ed94d1f01401a86906fc9713ee709?sid=b11c1f5a-66ff-4dbf-a83a-c9bea15a5d7b)
10
10
 
11
11
  </Card>
12
+
13
+ ## Installation
14
+
15
+ ### Using pip
16
+ ```
17
+ pip install corgea-cli
18
+ ```
19
+
20
+ ### Manual Installation
21
+ You can get the latest binaries for your OS from https://github.com/Corgea/cli/releases.
22
+
23
+ ### Setup
24
+ Once the binary is installed, login with your token from the Corgea app.
25
+ ```
26
+ corgea login <token>
27
+ ```
@@ -0,0 +1,7 @@
1
+ use std::env;
2
+
3
+ pub fn debug(input: &str) {
4
+ if env::var("DEBUG").is_ok() {
5
+ println!("DEBUG: {}", input);
6
+ }
7
+ }
@@ -1,9 +1,11 @@
1
1
  use reqwest;
2
2
  use std::collections::HashMap;
3
3
  use std::error::Error;
4
+ use crate::log::debug;
4
5
 
5
6
  pub fn verify_token(token: &str, corgea_url: &str) -> Result<bool, Box<dyn Error>> {
6
7
  let url = format!("{}/api/cli/verify/{}", corgea_url, token);
8
+ debug(&format!("GET: {}", url));
7
9
  let response = reqwest::blocking::get(url)?;
8
10
 
9
11
  if response.status().is_success() {
@@ -2,6 +2,7 @@ mod login;
2
2
  mod config;
3
3
  mod scan;
4
4
  mod cicd;
5
+ mod log;
5
6
 
6
7
  use std::str::FromStr;
7
8
  use clap::{Parser, Subcommand};
@@ -7,6 +7,7 @@ use uuid::Uuid;
7
7
  use std::path::Path;
8
8
  use std::process::Command;
9
9
  use crate::cicd::{*};
10
+ use crate::log::debug;
10
11
 
11
12
  pub fn run_command(base_cmd: &String, mut command: Command) -> String {
12
13
  match which::which(base_cmd) {
@@ -90,6 +91,8 @@ pub fn read_file_report(config: &Config, file_path: &str) {
90
91
  }
91
92
 
92
93
  pub fn parse_scan(config: &Config, input: String, save_to_file: bool) {
94
+ debug("Parsing the scan report json");
95
+
93
96
  let mut paths: Vec<String> = Vec::new();
94
97
  let mut scanner = String::new();
95
98
  let data: std::result::Result<Value, _> = serde_json::from_str(&input);
@@ -99,6 +102,7 @@ pub fn parse_scan(config: &Config, input: String, save_to_file: bool) {
99
102
  let schema = data.get("$schema").and_then(|v| v.as_str()).unwrap_or("unknown");
100
103
 
101
104
  if input.contains("semgrep.dev") {
105
+ debug("Detected semgrep schema");
102
106
  scanner = "semgrep".to_string();
103
107
  if let Some(results) = data.get("results").and_then(|v| v.as_array()) {
104
108
  for result in results {
@@ -108,13 +112,16 @@ pub fn parse_scan(config: &Config, input: String, save_to_file: bool) {
108
112
  }
109
113
  }
110
114
  } else if schema.contains("sarif") {
115
+ debug("Detected sarif schema");
111
116
  let run = data.get("runs").and_then(|v| v.as_array()).and_then(|v| v.get(0));
112
117
  let driver = run.and_then(|v| v.get("tool")).and_then(|v| v.get("driver")).and_then(|v| v.get("name"));
113
118
  let tool = driver.and_then(|v| v.as_str()).unwrap_or("unknown");
114
119
 
115
120
  if tool == "SnykCode" {
121
+ debug("Detected snyk version of sarif schema");
116
122
  scanner = "snyk".to_string();
117
123
  } else if tool == "CodeQL" {
124
+ debug("Detected codeql version of sarif schema");
118
125
  scanner = "codeql".to_string();
119
126
  } else {
120
127
  eprintln!("{} is not supported as this time.", tool);
@@ -136,8 +143,9 @@ pub fn parse_scan(config: &Config, input: String, save_to_file: bool) {
136
143
  }
137
144
  }
138
145
  }
139
- // checkmarx
140
- } else if input.contains("Cx") && data.get("results").is_some() && data.get("scanID").is_some() {
146
+ // checkmarx report generated by CLI
147
+ } else if data.get("totalCount").is_some() && data.get("results").is_some() && data.get("scanID").is_some() {
148
+ debug("Detected checkmarx cli schema");
141
149
  scanner = "checkmarx".to_string();
142
150
  if let Some(results) = data.get("results").and_then(|v| v.as_array()) {
143
151
  for result in results {
@@ -154,6 +162,37 @@ pub fn parse_scan(config: &Config, input: String, save_to_file: bool) {
154
162
  }
155
163
  }
156
164
  }
165
+ // for checkmarx report generated by web
166
+ } else if data.get("scanResults").is_some() && data.get("reportId").is_some() {
167
+ debug("Detected checkmarx web schema");
168
+ scanner = "checkmarx".to_string();
169
+ if let Some(scan_results) = data.get("scanResults") {
170
+ if let Some(sast) = scan_results.get("sast") {
171
+ if let Some(languges) = sast.get("languages").and_then(|v| v.as_array()) {
172
+ for language in languges {
173
+ if let Some(queries) = language.get("queries").and_then(|v| v.as_array()) {
174
+ for query in queries {
175
+ if let Some(vulns) = query.get("vulnerabilities").and_then(|v| v.as_array()) {
176
+ for vuln in vulns {
177
+ if let Some(nodes) = vuln.get("nodes").and_then(|v| v.as_array()) {
178
+ for node in nodes {
179
+ if let Some(path) = node.get("fileName") {
180
+ if let Some(truncated_path) = path.as_str() {
181
+ paths.push(truncated_path.get(1..).unwrap_or("").to_string());
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+ } else {
195
+ debug("Couldn't detect what kind of report this is.")
157
196
  }
158
197
  }
159
198
  Err(e) => {
@@ -183,6 +222,7 @@ fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input: Stri
183
222
  let project;
184
223
 
185
224
  if in_ci {
225
+ debug("Running in CI");
186
226
  project = format!("{}-{}",
187
227
  github_env_vars.get("GITHUB_REPOSITORY").expect("Failed to get GITHUB_REPOSITORY").to_string(),
188
228
  github_env_vars.get("GITHUB_PR").expect("Failed to get GITHUB_REPOSITORY").to_string())
@@ -196,11 +236,16 @@ fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input: Stri
196
236
  let git_config_upload_url = format!(
197
237
  "{}/api/cli/git-config-upload?token={}&run_id={}", base_url, token, run_id
198
238
  );
199
- let client = reqwest::blocking::Client::new();
239
+ let client = reqwest::blocking::Client::builder()
240
+ .timeout(std::time::Duration::from_secs(5 * 60))
241
+ .build()
242
+ .expect("Failed to build client");
200
243
 
201
244
  println!("Uploading required files for the scan...");
202
245
 
203
246
  let mut uploaded_paths = HashSet::new();
247
+ let mut uploaded_count = 0;
248
+ let mut upload_error_count = 0;
204
249
 
205
250
  for path in &paths {
206
251
  if !Path::new(&path).exists() {
@@ -215,36 +260,56 @@ fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input: Stri
215
260
  let src_upload_url = format!(
216
261
  "{}/api/cli/code-upload?token={}&run_id={}&path={}", base_url, token, run_id, path
217
262
  );
263
+ debug(&format!("Uploading file: {}", path));
218
264
  let fp = Path::new(&path);
219
265
 
220
- let form = reqwest::blocking::multipart::Form::new()
221
- .file("file", fp)
222
- .expect("Failed to read file");
223
-
224
- let client = reqwest::blocking::Client::new();
225
- let res = client.post(&src_upload_url)
226
- .multipart(form)
227
- .send();
228
-
229
- match res {
230
- Ok(response) => {
231
- if !response.status().is_success() {
232
- eprintln!("Failed to upload file: {}", response.status());
266
+ let mut attempts = 0;
267
+ let mut success = false;
268
+
269
+ while attempts < 3 && !success {
270
+ let form = reqwest::blocking::multipart::Form::new()
271
+ .file("file", fp)
272
+ .expect("Failed to read file");
273
+
274
+ debug(&format!("POST: {}", src_upload_url));
275
+ let res = client.post(&src_upload_url)
276
+ .multipart(form)
277
+ .send();
278
+
279
+ match res {
280
+ Ok(response) => {
281
+ if !response.status().is_success() {
282
+ eprintln!("Failed to upload file {} {}... retrying", response.status(), path);
283
+ std::thread::sleep(std::time::Duration::from_secs(1));
284
+ upload_error_count = upload_error_count + 1;
285
+ attempts += 1;
286
+ } else {
287
+ uploaded_count += 1;
288
+ success = true;
289
+ uploaded_paths.insert(path.clone());
290
+ }
291
+ }
292
+ Err(e) => {
293
+ eprintln!("Failed to send request: {}", e);
233
294
  std::process::exit(1);
234
- } else {
235
- uploaded_paths.insert(path.clone());
236
295
  }
237
296
  }
238
- Err(e) => {
239
- eprintln!("Failed to send request: {}", e);
240
- std::process::exit(1);
241
- }
242
297
  }
298
+
299
+ if attempts == 3 && !success {
300
+ eprintln!("Failed to upload file: {} after 3 attempts. skipping...", path);
301
+ }
302
+ }
303
+
304
+ if uploaded_count == 0 {
305
+ eprintln!("Failed to upload any files for the scan, exiting.");
306
+ std::process::exit(1);
243
307
  }
244
308
 
245
309
  println!("Uploading the scan...");
246
310
 
247
311
  // main scan upload
312
+ debug(&format!("POST: {}", scan_upload_url));
248
313
  let res = client.post(scan_upload_url)
249
314
  .header(header::CONTENT_TYPE, "application/json")
250
315
  .body(input.clone())
@@ -267,11 +332,12 @@ fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input: Stri
267
332
  let git_config_path = Path::new(".git/config");
268
333
 
269
334
  if git_config_path.exists() {
335
+ debug("Uploading .git/config");
270
336
  let form = reqwest::blocking::multipart::Form::new()
271
337
  .file("file", git_config_path)
272
338
  .expect("Failed to read file");
273
339
 
274
- let client = reqwest::blocking::Client::new();
340
+ debug(&format!("POST: {}", git_config_upload_url));
275
341
  let res = client.post(&git_config_upload_url)
276
342
  .multipart(form)
277
343
  .send();
@@ -306,6 +372,7 @@ fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input: Stri
306
372
  }
307
373
  };
308
374
 
375
+ debug(&format!("POST: {}", ci_data_upload_url));
309
376
  let _res = client.post(ci_data_upload_url)
310
377
  .header(header::CONTENT_TYPE, "application/json")
311
378
  .body(github_env_vars_json_string)
@@ -323,5 +390,10 @@ fn upload_scan(config: &Config, paths: Vec<String>, scanner: String, input: Stri
323
390
  }
324
391
 
325
392
  println!("Successfully scanned using {} and uploaded to Corgea.", scanner);
393
+
394
+ if upload_error_count > 0 {
395
+ println!("Failed to upload {} files, you may not see all fixes in Corgea.", upload_error_count);
396
+ }
397
+
326
398
  println!("Go to {base_url} to see results.");
327
399
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes