Skip to content
This repository was archived by the owner on Aug 25, 2024. It is now read-only.

Commit ea9cb32

Browse files
committed
shouldi: rust: Fix identification
The rust package we picked to evaluate (crates.io) contains both a package.json and a Cargo.toml. We were getting the results of the npm audit scan rather than the cargo audit scan due to there being the package.json file in the project's root. As well as the fact that the cargo audit wasn't running. Due to it checking for cargo.toml rather than Cargo.toml. This patch checks for both upper and lower case Cargo.toml files. We also switch to using GetMulti rather than GetSingle to get all static analysis scans that may have occurred. Fixes: #826 Signed-off-by: John Andersen <[email protected]>
1 parent f341176 commit ea9cb32

File tree

6 files changed

+90
-22
lines changed

6 files changed

+90
-22
lines changed

examples/shouldi/shouldi/rust/analyze.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,9 @@ async def analyze_rust(self, repo):
3535
]
3636
):
3737
cargo_report = results[run_cargo_audit.op.outputs["report"].name]
38-
return {"result": SA_RESULTS.spec(critical=cargo_report)}
38+
39+
return {
40+
"result": SA_RESULTS.spec(
41+
report=cargo_report, **cargo_report["qualitative"]
42+
)
43+
}

examples/shouldi/shouldi/rust/cargo_audit.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import os
12
import json
23
import asyncio
4+
import itertools
5+
import contextlib
36
from typing import Dict, Any
47

5-
from dffml import op, Definition
8+
from dffml import op, Definition, traverse_get
69

710
package_src_dir = Definition(name="package_src_dir", primitive="str")
811
cargo_audit_output = Definition(
@@ -48,8 +51,48 @@ async def run_cargo_audit(pkg: str) -> Dict[str, Any]:
4851
if len(stdout) == 0:
4952
raise CargoAuditError(stderr.decode())
5053

51-
cargo_audit_op = stdout.decode()
52-
issues = json.loads(cargo_audit_op)
53-
result = issues["vulnerabilities"]["count"]
54+
cargo_report = json.loads(stdout.decode())
5455

55-
return {"report": result}
56+
spec = {
57+
"low": 0,
58+
"medium": 0,
59+
"high": 0,
60+
"critical": 0,
61+
}
62+
63+
for vuln in itertools.chain(
64+
cargo_report["vulnerabilities"]["list"], cargo_report["warnings"],
65+
):
66+
cvss = None
67+
for path in [
68+
("kind", "unmaintained", "advisory", "cvss"),
69+
("advisory", "cvss"),
70+
]:
71+
with contextlib.suppress(KeyError):
72+
cvss = traverse_get(vuln, *path)
73+
if cvss is None or isinstance(cvss, (float, int)):
74+
break
75+
if cvss is None:
76+
cvss = 0
77+
78+
# Rating | CVSS Score
79+
# ---------+-------------
80+
# None | 0.0
81+
# Low | 0.1 - 3.9
82+
# Medium | 4.0 - 6.9
83+
# High | 7.0 - 8.9
84+
# Critical | 9.0 - 10.0
85+
#
86+
# Source: https://www.first.org/cvss/specification-document
87+
if cvss >= 9.0:
88+
spec["critical"] += 1
89+
elif cvss >= 7.0:
90+
spec["high"] += 1
91+
elif cvss >= 4.0:
92+
spec["medium"] += 1
93+
else:
94+
spec["low"] += 1
95+
96+
cargo_report["qualitative"] = spec
97+
98+
return {"report": cargo_report}

examples/shouldi/shouldi/rust/check.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
},
1212
)
1313
def has_package_cargo(repo: clone_git_repo.op.outputs["repo"].spec):
14-
return {"result": pathlib.Path(repo.directory, "cargo.toml").is_file()}
14+
return {
15+
"result": pathlib.Path(repo.directory, "cargo.toml").is_file()
16+
or pathlib.Path(repo.directory, "Cargo.toml").is_file()
17+
}
1518

1619

1720
DATAFLOW = DataFlow.auto(has_package_cargo, GetSingle)

examples/shouldi/shouldi/use.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import List
33

44
# Command line utility helpers and DataFlow specific classes
5-
from dffml import CMD, DataFlow, Input, GetSingle, run, config, field
5+
from dffml import CMD, DataFlow, Input, GetMulti, run, config, field
66

77
# Allow users to give us a Git repo URL and we'll clone it and turn it into a
88
# directory that will be scanned
@@ -33,10 +33,10 @@
3333
check_rust,
3434
analyze_rust,
3535
cleanup_git_repo,
36-
GetSingle,
36+
GetMulti,
3737
)
3838
DATAFLOW.seed.append(
39-
Input(value=[SA_RESULTS.name,], definition=GetSingle.op.inputs["spec"],)
39+
Input(value=[SA_RESULTS.name,], definition=GetMulti.op.inputs["spec"],)
4040
)
4141

4242
# Allow for directory to be provided by user instead of the result of cloning a

examples/shouldi/tests/test_cargo_audit.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@ async def test_run(self, rust, cargo_audit, crates):
3131
/ "crates.io-8c1a7e29073e175f0e69e0e537374269da244cee"
3232
)
3333
)
34-
self.assertEqual(results["report"], 1)
34+
self.assertEqual(
35+
len(results["report"]["vulnerabilities"]["list"]), 1
36+
)

examples/shouldi/tests/test_cli_use.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import io
22
import pathlib
3+
import subprocess
34
from unittest.mock import patch
45

56
from dffml import prepend_to_path, AsyncTestCase
@@ -46,19 +47,30 @@ async def test_use_javascript(self, node, javascript_algo):
4647
@cached_cargo_audit
4748
@cached_target_crates
4849
async def test_use_rust(self, rust, cargo_audit, crates):
49-
if not (
50-
cargo_audit
51-
/ "cargo-audit-0.11.2"
52-
/ "target"
53-
/ "release"
54-
/ "cargo-audit"
55-
).is_file():
56-
await run_cargo_build(cargo_audit / "cargo-audit-0.11.2")
57-
50+
if not (rust / "rust-install" / "bin" / "cargo").is_file():
51+
subprocess.check_call(
52+
[
53+
str(
54+
rust
55+
/ "rust-1.42.0-x86_64-unknown-linux-gnu"
56+
/ "install.sh"
57+
),
58+
f"--prefix={(rust / 'rust-install').resolve()}",
59+
]
60+
)
5861
with prepend_to_path(
59-
rust / "rust-1.42.0-x86_64-unknown-linux-gnu" / "cargo" / "bin",
62+
rust / "rust-install" / "bin",
6063
cargo_audit / "cargo-audit-0.11.2" / "target" / "release",
6164
):
65+
if not (
66+
cargo_audit
67+
/ "cargo-audit-0.11.2"
68+
/ "target"
69+
/ "release"
70+
/ "cargo-audit"
71+
).is_file():
72+
await run_cargo_build(cargo_audit / "cargo-audit-0.11.2")
73+
6274
with patch("sys.stdout", new_callable=io.StringIO) as stdout:
6375
await ShouldI.cli(
6476
"use",
@@ -68,4 +80,7 @@ async def test_use_rust(self, rust, cargo_audit, crates):
6880
),
6981
)
7082
output = stdout.getvalue()
71-
self.assertIn("high=2", output)
83+
# cargo audit
84+
self.assertIn("low=2,", output)
85+
# npm audit
86+
self.assertIn("high=6,", output)

0 commit comments

Comments
 (0)