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.
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/Cargo.lock +1 -1
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/Cargo.toml +1 -1
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/PKG-INFO +17 -1
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/README.md +16 -0
- corgea_cli-1.3.2/src/log.rs +7 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/src/login.rs +2 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/src/main.rs +1 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/src/scan.rs +95 -23
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/.github/workflows/release.yml +0 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/.gitignore +0 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/LICENSE +0 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/build_release.sh +0 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/pyproject.toml +0 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/src/cicd.rs +0 -0
- {corgea_cli-1.3.0 → corgea_cli-1.3.2}/src/config.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: corgea-cli
|
|
3
|
-
Version: 1.3.
|
|
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://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
|
+
```
|
|
@@ -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() {
|
|
@@ -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
|
-
|
|
140
|
-
} else if
|
|
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::
|
|
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
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
|
File without changes
|