Skip to content

Commit 1d7267e

Browse files
author
JonasPollokZweitag
committed
chore: add scan report -> ctrf converter
1 parent 691003c commit 1d7267e

File tree

3 files changed

+238
-0
lines changed

3 files changed

+238
-0
lines changed

security-scanning/checkov2ctrf.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import json
2+
import sys
3+
4+
5+
def extract_checks(target, status):
6+
# extracts checks of a check_type (e.g. terraform) with a status ('fail' oder 'pass').
7+
checks = []
8+
key = "failed_checks" if status == "fail" else "passed_checks"
9+
for check in target.get("results", {}).get(key, []):
10+
checks.append({
11+
"name": check.get("check_name"),
12+
"status": "passed" if status == "pass" else "failed",
13+
"duration": 1,
14+
"file": check.get("file_path"),
15+
"lines": check.get("file_line_range"),
16+
"guideline": check.get("guideline") if check.get("guideline") != 'null' else "",
17+
})
18+
return checks
19+
20+
21+
def checkov_to_ctrf(checkov_json):
22+
tests = []
23+
for target in checkov_json:
24+
tests.extend(extract_checks(target, "fail"))
25+
tests.extend(extract_checks(target, "pass"))
26+
27+
total = len(tests)
28+
passed = sum(1 for t in tests if t["status"] == "passed")
29+
failed = sum(1 for t in tests if t["status"] == "failed")
30+
pending = 0
31+
skipped = 0
32+
other = 0
33+
start = 0
34+
stop = 1
35+
return {
36+
"results": {
37+
"tool": {
38+
"name": "Checkov "
39+
},
40+
"summary": {
41+
"tests": total,
42+
"passed": passed,
43+
"failed": failed,
44+
"pending": pending,
45+
"skipped": skipped,
46+
"other": other,
47+
"start": start,
48+
"stop": stop
49+
},
50+
"tests": tests,
51+
"environment": {
52+
"appName": "kamium-deployment",
53+
"buildName": "kamium-deployment",
54+
"buildNumber": "1"
55+
}
56+
}
57+
}
58+
59+
60+
if __name__ == "__main__":
61+
if len(sys.argv) != 3:
62+
print("Usage: python checkov2ctrf.py <input.json> <output.json>")
63+
sys.exit(1)
64+
with open(sys.argv[1]) as old_f:
65+
checkov_json = json.load(old_f)
66+
ctrf_json = checkov_to_ctrf(checkov_json)
67+
with open(sys.argv[2], "w") as new_f:
68+
json.dump(ctrf_json, new_f, indent=2)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import json
2+
import sys
3+
4+
5+
def extract_checks_from_trivy_result(result):
6+
checks = []
7+
misconfigs = result.get("Misconfigurations", [])
8+
for misconf in misconfigs:
9+
lines = None
10+
cause = misconf.get("CauseMetadata", {})
11+
if "StartLine" in cause and "EndLine" in cause:
12+
lines = [cause["StartLine"], cause["EndLine"]]
13+
elif "Code" in cause and "Lines" in cause["Code"] and cause["Code"]["Lines"]:
14+
# Fallback: nehme die ersten und letzten Zeilennummern aus Code.Lines
15+
code_lines = cause["Code"]["Lines"]
16+
if isinstance(code_lines, list) and code_lines:
17+
lines = [code_lines[0].get(
18+
"Number"), code_lines[-1].get("Number")]
19+
20+
checks.append({
21+
"name": misconf.get("Title", misconf.get("ID", "")),
22+
"status": "failed" if misconf.get("Status") == "FAIL" else "passed",
23+
"duration": 1,
24+
"file": result.get("Target", ""),
25+
"lines": lines,
26+
"guideline": misconf.get("PrimaryURL", ""),
27+
"severity": misconf.get("Severity", ""),
28+
"description": misconf.get("Description", ""),
29+
"message": misconf.get("Message", ""),
30+
"resolution": misconf.get("Resolution", ""),
31+
"references": misconf.get("References", []),
32+
"type": misconf.get("Type", ""),
33+
"id": misconf.get("ID", ""),
34+
})
35+
return checks
36+
37+
38+
def trivy_to_ctrf(trivy_json):
39+
tests = []
40+
successes_sum = 0
41+
results = trivy_json.get("Results", [])
42+
for result in results:
43+
tests.extend(extract_checks_from_trivy_result(result))
44+
45+
# Successful scans have no misconfigurations
46+
misconf_summary = result.get("MisconfSummary", {})
47+
successes_sum += misconf_summary.get("Successes", 0)
48+
49+
total = len(tests) + successes_sum
50+
passed = successes_sum
51+
failed = sum(1 for t in tests if t["status"] == "failed")
52+
pending = 0
53+
skipped = 0
54+
other = 0
55+
start = 0
56+
stop = 1
57+
return {
58+
"results": {
59+
"tool": {
60+
"name": "Trivy Configuration"
61+
},
62+
"summary": {
63+
"tests": total,
64+
"passed": passed,
65+
"failed": failed,
66+
"pending": pending,
67+
"skipped": skipped,
68+
"other": other,
69+
"start": start,
70+
"stop": stop
71+
},
72+
"tests": tests,
73+
"environment": {
74+
"appName": "kamium-elastic",
75+
"buildName": "kamium-elastic",
76+
"buildNumber": "1"
77+
}
78+
}
79+
}
80+
81+
82+
if __name__ == "__main__":
83+
if len(sys.argv) != 3:
84+
print("Usage: python trivy2ctrf.py <input.json> <output.json>")
85+
sys.exit(1)
86+
with open(sys.argv[1]) as old_f:
87+
trivy_json = json.load(old_f)
88+
ctrf_json = trivy_to_ctrf(trivy_json)
89+
with open(sys.argv[2], "w") as new_f:
90+
json.dump(ctrf_json, new_f, indent=2)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import json
2+
import sys
3+
4+
5+
def extract_checks_from_trivy_result(target):
6+
checks = []
7+
8+
vulnerabilities = target.get("Vulnerabilities", [])
9+
10+
for vuln in vulnerabilities:
11+
checks.append({
12+
"name": vuln.get("PkgID", ""),
13+
"status": vuln.get("Status", "unknown"),
14+
"duration": 1,
15+
"severity": vuln.get("Severity", ""),
16+
"id": vuln.get("VulnerabilityID", ""),
17+
"pkgName": vuln.get("PkgName", ""),
18+
"installedVersion": vuln.get("InstalledVersion", ""),
19+
"fixedVersion": vuln.get("FixedVersion", "no fix available"),
20+
"image": target.get("Target", ""),
21+
"source": vuln.get("DataSource", []),
22+
"description": vuln.get("Description", ""),
23+
"references": vuln.get("References", [])
24+
})
25+
return checks
26+
27+
28+
def trivy_to_ctrf(trivy_json):
29+
tests = []
30+
successes_sum = 0
31+
results = trivy_json.get("Results", [])
32+
for result in results:
33+
tests.extend(extract_checks_from_trivy_result(result))
34+
35+
# Successful scans have no misconfigurations
36+
misconf_summary = result.get("MisconfSummary", {})
37+
successes_sum += misconf_summary.get("Successes", 0)
38+
39+
total = len(tests)
40+
passed = 0
41+
failed = len(tests)
42+
pending = 0
43+
skipped = 0
44+
other = 0
45+
start = 0
46+
stop = 1
47+
return {
48+
"results": {
49+
"tool": {
50+
"name": "Trivy Image"
51+
},
52+
"summary": {
53+
"tests": total,
54+
"passed": passed,
55+
"failed": failed,
56+
"pending": pending,
57+
"skipped": skipped,
58+
"other": other,
59+
"start": start,
60+
"stop": stop
61+
},
62+
"tests": tests,
63+
"environment": {
64+
"appName": "kamium-elastic",
65+
"buildName": "kamium-elastic",
66+
"buildNumber": "1"
67+
}
68+
}
69+
}
70+
71+
72+
if __name__ == "__main__":
73+
if len(sys.argv) != 3:
74+
print("Usage: python trivy2ctrf.py <input.json> <output.json>")
75+
sys.exit(1)
76+
with open(sys.argv[1]) as old_f:
77+
trivy_json = json.load(old_f)
78+
ctrf_json = trivy_to_ctrf(trivy_json)
79+
with open(sys.argv[2], "w") as new_f:
80+
json.dump(ctrf_json, new_f, indent=2)

0 commit comments

Comments
 (0)