Skip to content

Commit 3874e6e

Browse files
Merge branch 'bugfix' into implement_oidc_groups
2 parents e774dde + 2171863 commit 3874e6e

34 files changed

+558
-120
lines changed

.github/renovate.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,27 @@
2727
"matchDatasources": "github-releases",
2828
"matchPackageNames": "renovatebot/renovate",
2929
"schedule": ["* * * * 0"]
30+
},{
31+
"description": "Minikube does not like freshly released k8s. We need to wait some time so it will be adopted",
32+
"matchDatasources": [
33+
"custom.endoflife-oldest-maintained",
34+
"github-releases"
35+
],
36+
"matchPackageNames": [
37+
"kubernetes",
38+
"kubernetes/kubernetes"
39+
],
40+
"minimumReleaseAge": "2 days"
3041
}],
42+
"customDatasources": {
43+
"endoflife-oldest-maintained": {
44+
"defaultRegistryUrlTemplate": "https://endoflife.date/api/v1/products/{{packageName}}",
45+
"format": "json",
46+
"transformTemplates": [
47+
"{ \"releases\": [$.result.releases[isMaintained = true]^(<eolFrom)[0].latest.{\"version\": name, \"releaseTimestamp\": date, \"changelogUrl\": link}], \"sourceUrl\": \"https://github.com/kubernetes/kubernetes\", \"homepage\": \"https://kubernetes.io/\" }"
48+
]
49+
}
50+
},
3151
"customManagers": [
3252
{
3353
"customType": "regex",

.github/workflows/k8s-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
# are tested (https://kubernetes.io/releases/)
1919
- k8s: 'v1.34.0' # renovate: datasource=github-releases depName=kubernetes/kubernetes versioning=loose
2020
os: debian
21-
- k8s: 'v1.31.13' # Do not track with renovate as we likely want to rev this manually
21+
- k8s: 'v1.31.13' # renovate: datasource=custom.endoflife-oldest-maintained depName=kubernetes
2222
os: debian
2323
steps:
2424
- name: Checkout

.github/workflows/test-helm-chart.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ jobs:
115115
if: startsWith(github.head_ref, 'renovate/') || startsWith(github.head_ref, 'dependabot/')
116116
run: |
117117
yq -i '.annotations."artifacthub.io/changes" += "- kind: changed\n description: ${{ github.event.pull_request.title }}\n"' helm/defectdojo/Chart.yaml
118+
git add helm/defectdojo/Chart.yaml
119+
git commit -m "ci: update Chart annotations from PR #${{ github.event.pull_request.number }}" || echo "No changes to commit"
118120
119121
- name: Run helm-docs (update)
120122
uses: losisin/helm-docs-github-action@a57fae5676e4c55a228ea654a1bcaec8dd3cf5b5 # v1.6.2
@@ -128,7 +130,7 @@ jobs:
128130
# The helm-docs documentation will be generated for you.
129131
- name: Run helm-docs (check)
130132
uses: losisin/helm-docs-github-action@a57fae5676e4c55a228ea654a1bcaec8dd3cf5b5 # v1.6.2
131-
if: ! startsWith(github.head_ref, 'renovate/') || startsWith(github.head_ref, 'dependabot/')
133+
if: ${{ !(startsWith(github.head_ref, 'renovate/') || startsWith(github.head_ref, 'dependabot/')) }}
132134
with:
133135
fail-on-diff: true
134136
chart-search-root: "helm/defectdojo"

dojo/settings/settings.dist.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,7 @@ def saml2_attrib_map_format(din):
19521952
"TS-": """https://tailscale.com/security-bulletins#""", # e.g. https://tailscale.com/security-bulletins or https://tailscale.com/security-bulletins#ts-2022-001-1243
19531953
"TYPO3-": "https://typo3.org/security/advisory/", # e.g. https://typo3.org/security/advisory/typo3-core-sa-2025-010
19541954
"USN-": "https://ubuntu.com/security/notices/", # e.g. https://ubuntu.com/security/notices/USN-6642-1
1955+
"VA-": "https://cvepremium.circl.lu/vuln/", # e.g. https://cvepremium.circl.lu/vuln/va-25-282-01
19551956
"VAR-": "https://cvepremium.circl.lu/vuln/", # e.g. https://cvepremium.circl.lu/vuln/var-201801-0152
19561957
"VNS": "https://vulners.com/",
19571958
"WID-SEC-W-": "https://cvepremium.circl.lu/vuln/", # e.g. https://cvepremium.circl.lu/vuln/wid-sec-w-2025-1468

dojo/tools/dawnscanner/parser.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ def get_findings(self, filename, test):
3030
if item["message"][0:2] != "b,"
3131
else item["message"][0:-1]
3232
)
33-
3433
finding = Finding(
3534
title=item["name"],
3635
test=test,
@@ -42,6 +41,10 @@ def get_findings(self, filename, test):
4241
static_finding=True,
4342
dynamic_finding=False,
4443
)
44+
if item.get("remediation"):
45+
finding.fix_available = True
46+
else:
47+
finding.fix_available = False
4548

4649
if self.CVE_REGEX.match(item["name"]):
4750
finding.unsaved_vulnerability_ids = [

dojo/tools/deepfence_threatmapper/compliance.py

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33

44
class DeepfenceThreatmapperCompliance:
55
def get_findings(self, row, headers, test):
6-
description = ""
6+
if "compliance_check_type" in headers and "test_number" in headers:
7+
return self._parse_old_format(row, headers, test)
8+
if "Compliance Standard" in headers and "Control ID" in headers:
9+
return self._parse_new_format(row, headers, test)
10+
return None
11+
12+
def _parse_old_format(self, row, headers, test):
713
compliance_check_type = row[headers["compliance_check_type"]]
814
count = row[headers["count"]]
915
doc_id = row[headers["doc_id"]]
@@ -18,34 +24,76 @@ def get_findings(self, row, headers, test):
1824
test_desc = row[headers["test_desc"]]
1925
test_info = row[headers["test_info"]]
2026
test_number = row[headers["test_number"]]
21-
description += "**compliance_check_type:** " + str(compliance_check_type) + "\n"
22-
description += "**host_name:** " + str(host_name) + "\n"
23-
description += "**cloud_account_id:** " + str(cloud_account_id) + "\n"
24-
description += "**masked:** " + str(masked) + "\n"
25-
description += "**node_id:** " + str(node_id) + "\n"
26-
description += "**node_name:** " + str(node_name) + "\n"
27-
description += "**node_type:** " + str(node_type) + "\n"
28-
description += "**status:** " + str(status) + "\n"
29-
description += "**test_category:** " + str(test_category) + "\n"
30-
description += "**test_desc:** " + str(test_desc) + "\n"
31-
description += "**test_info:** " + str(test_info) + "\n"
32-
description += "**test_number:** " + str(test_number) + "\n"
33-
description += "**count:** " + str(count) + "\n"
34-
description += "**doc_id:** " + str(doc_id) + "\n"
27+
28+
description = (
29+
f"**Compliance Check Type:** {compliance_check_type}\n"
30+
f"**Host Name:** {host_name}\n"
31+
f"**Cloud Account ID:** {cloud_account_id}\n"
32+
f"**Masked:** {masked}\n"
33+
f"**Node ID:** {node_id}\n"
34+
f"**Node Name:** {node_name}\n"
35+
f"**Node Type:** {node_type}\n"
36+
f"**Status:** {status}\n"
37+
f"**Test Category:** {test_category}\n"
38+
f"**Test Description:** {test_desc}\n"
39+
f"**Test Info:** {test_info}\n"
40+
f"**Test Number:** {test_number}\n"
41+
f"**Count:** {count}\n"
42+
f"**Doc ID:** {doc_id}\n"
43+
)
44+
45+
return Finding(
46+
title=f"Threatmapper_Compliance_Report-{test_number}",
47+
description=description,
48+
severity=self.compliance_severity(status),
49+
static_finding=False,
50+
dynamic_finding=True,
51+
test=test,
52+
)
53+
54+
def _parse_new_format(self, row, headers, test):
55+
compliance_standard = row[headers["Compliance Standard"]]
56+
status = row[headers["Status"]]
57+
category = row[headers["Category"]]
58+
description_text = row[headers["Description"]]
59+
info = row[headers["Info"]]
60+
control_id = row[headers["Control ID"]]
61+
node_name = row[headers["Node Name"]]
62+
node_type = row[headers["Node Type"]]
63+
remediation = row[headers["Remediation"]]
64+
masked = row[headers["Masked"]]
65+
66+
description = (
67+
f"**Compliance Standard:** {compliance_standard}\n"
68+
f"**Status:** {status}\n"
69+
f"**Category:** {category}\n"
70+
f"**Description:** {description_text}\n"
71+
f"**Info:** {info}\n"
72+
f"**Control ID:** {control_id}\n"
73+
f"**Node Name:** {node_name}\n"
74+
f"**Node Type:** {node_type}\n"
75+
f"**Remediation:** {remediation}\n"
76+
f"**Masked:** {masked}\n"
77+
)
78+
3579
return Finding(
36-
title="Threatmapper_Compliance_Report-" + test_number,
80+
title=f"Threatmapper_Compliance_Report-{control_id}",
3781
description=description,
3882
severity=self.compliance_severity(status),
3983
static_finding=False,
4084
dynamic_finding=True,
85+
mitigation=remediation,
4186
test=test,
4287
)
4388

4489
def compliance_severity(self, severity_input):
90+
if severity_input is None:
91+
return "Info"
92+
severity_input = severity_input.lower()
4593
if severity_input in {"pass", "info"}:
46-
output = "Info"
47-
elif severity_input == "warn":
48-
output = "Medium"
49-
else:
50-
output = "Info"
51-
return output
94+
return "Info"
95+
if severity_input == "warn":
96+
return "Medium"
97+
if severity_input == "fail":
98+
return "High"
99+
return "Info"

dojo/tools/deepfence_threatmapper/malware.py

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33

44
class DeepfenceThreatmapperMalware:
55
def get_findings(self, row, headers, test):
6-
description = ""
6+
if "Rule Name" in headers and "Class" in headers:
7+
return self._parse_old_format(row, headers, test)
8+
if "Rule Name" in headers and "Node Type" in headers:
9+
return self._parse_new_format(row, headers, test)
10+
return None
11+
12+
def _parse_old_format(self, row, headers, test):
713
Rule_Name = row[headers["Rule Name"]]
814
Class = row[headers["Class"]]
915
File_Name = row[headers["File Name"]]
@@ -13,14 +19,48 @@ def get_findings(self, row, headers, test):
1319
NodeType = row[headers["NodeType"]]
1420
Container_Name = row[headers["Container Name"]]
1521
Kubernetes_Cluster_Name = row[headers["Kubernetes Cluster Name"]]
16-
description += "**Summary:** " + str(Summary) + "\n"
17-
description += "**Rule Name:** " + str(Rule_Name) + "\n"
18-
description += "**Class:** " + str(Class) + "\n"
19-
description += "**File Name:** " + str(File_Name) + "\n"
20-
description += "**Node Name:** " + str(Node_Name) + "\n"
21-
description += "**NodeType:** " + str(NodeType) + "\n"
22-
description += "**Container Name:** " + str(Container_Name) + "\n"
23-
description += "**Kubernetes Cluster Name:** " + str(Kubernetes_Cluster_Name) + "\n"
22+
23+
description = (
24+
f"**Summary:** {Summary}\n"
25+
f"**Rule Name:** {Rule_Name}\n"
26+
f"**Class:** {Class}\n"
27+
f"**File Name:** {File_Name}\n"
28+
f"**Node Name:** {Node_Name}\n"
29+
f"**NodeType:** {NodeType}\n"
30+
f"**Container Name:** {Container_Name}\n"
31+
f"**Kubernetes Cluster Name:** {Kubernetes_Cluster_Name}\n"
32+
)
33+
34+
return Finding(
35+
title=Rule_Name,
36+
description=description,
37+
file_path=File_Name,
38+
severity=self.severity(Severity),
39+
static_finding=False,
40+
dynamic_finding=True,
41+
test=test,
42+
)
43+
44+
def _parse_new_format(self, row, headers, test):
45+
Rule_Name = row[headers["Rule Name"]]
46+
File_Name = row[headers["File Name"]]
47+
Summary = row[headers["Summary"]]
48+
Severity = row[headers["Severity"]]
49+
Node_Name = row[headers["Node Name"]]
50+
Node_Type = row[headers["Node Type"]]
51+
Kubernetes_Cluster_Name = row[headers["Kubernetes Cluster Name"]]
52+
Masked = row[headers["Masked"]]
53+
54+
description = (
55+
f"**Summary:** {Summary}\n"
56+
f"**Rule Name:** {Rule_Name}\n"
57+
f"**File Name:** {File_Name}\n"
58+
f"**Node Name:** {Node_Name}\n"
59+
f"**Node Type:** {Node_Type}\n"
60+
f"**Kubernetes Cluster Name:** {Kubernetes_Cluster_Name}\n"
61+
f"**Masked:** {Masked}\n"
62+
)
63+
2464
return Finding(
2565
title=Rule_Name,
2666
description=description,

dojo/tools/deepfence_threatmapper/parser.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,23 @@ def get_findings(self, filename, test):
2727
first = False
2828
for i in range(len(row)):
2929
headers[row[i]] = i
30-
elif headers.get("Rule Name") is not None and headers.get("Class") is not None:
30+
elif (
31+
("Rule Name" in headers and "Class" in headers) or
32+
("Rule Name" in headers and "Node Type" in headers)
33+
):
3134
findings.append(DeepfenceThreatmapperMalware().get_findings(row, headers, test))
3235
elif headers.get("Filename") is not None and headers.get("Content") is not None:
3336
value = DeepfenceThreatmapperSecret().get_findings(row, headers, test)
3437
if value is not None:
3538
findings.append(value)
36-
elif headers.get("@timestamp") is not None and headers.get("cve_attack_vector") is not None:
39+
elif (
40+
("cve_id" in headers and "cve_attack_vector" in headers) or
41+
("CVE ID" in headers and "Attack Vector" in headers)
42+
):
3743
findings.append(DeepfenceThreatmapperVulnerability().get_findings(row, headers, test))
38-
elif headers.get("@timestamp") is not None and headers.get("compliance_check_type") is not None:
44+
elif (
45+
("compliance_check_type" in headers and "test_number" in headers) or
46+
("Compliance Standard" in headers and "Control ID" in headers)
47+
):
3948
findings.append(DeepfenceThreatmapperCompliance().get_findings(row, headers, test))
4049
return findings

dojo/tools/deepfence_threatmapper/secret.py

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33

44
class DeepfenceThreatmapperSecret:
55
def get_findings(self, row, headers, test):
6+
if "Name" in headers and "Signature" in headers:
7+
return self._parse_old_format(row, headers, test)
8+
if "Content Starting Index" in headers and "Masked" in headers:
9+
return self._parse_new_format(row, headers, test)
10+
return None
11+
12+
def _parse_old_format(self, row, headers, test):
613
description = ""
714
Filename = row[headers["Filename"]]
815
Content = row[headers["Content"]]
@@ -13,27 +20,57 @@ def get_findings(self, row, headers, test):
1320
Container_Name = row[headers["Container Name"]]
1421
Kubernetes_Cluster_Name = row[headers["Kubernetes Cluster Name"]]
1522
Signature = row[headers["Signature"]]
16-
description += "**Filename:** " + str(Filename) + "\n"
17-
description += "**Name:** " + str(Name) + "\n"
18-
description += "**Rule:** " + str(Rule) + "\n"
19-
description += "**Node Name:** " + str(Node_Name) + "\n"
20-
description += "**Container Name:** " + str(Container_Name) + "\n"
21-
description += "**Kubernetes Cluster Name:** " + str(Kubernetes_Cluster_Name) + "\n"
22-
description += "**Content:** " + str(Content) + "\n"
23-
description += "**Signature:** " + str(Signature) + "\n"
24-
if Name is not None and Severity is not None:
25-
finding = Finding(
26-
title=str(Name),
27-
description=description,
28-
file_path=Filename,
29-
severity=self.severity(Severity),
30-
static_finding=False,
31-
dynamic_finding=True,
32-
test=test,
23+
description += f"**Filename:** {Filename}\n"
24+
description += f"**Name:** {Name}\n"
25+
description += f"**Rule:** {Rule}\n"
26+
description += f"**Node Name:** {Node_Name}\n"
27+
description += f"**Container Name:** {Container_Name}\n"
28+
description += f"**Kubernetes Cluster Name:** {Kubernetes_Cluster_Name}\n"
29+
description += f"**Content:** {Content}\n"
30+
description += f"**Signature:** {Signature}\n"
31+
if Name and Severity:
32+
return Finding(
33+
title=str(Name),
34+
description=description,
35+
file_path=Filename,
36+
severity=self.severity(Severity),
37+
static_finding=False,
38+
dynamic_finding=True,
39+
test=test,
40+
)
41+
return None
42+
43+
def _parse_new_format(self, row, headers, test):
44+
description = ""
45+
Filename = row[headers["Filename"]]
46+
Content = row[headers["Content"]]
47+
Rule = row[headers["Rule"]]
48+
Severity = row[headers["Severity"]]
49+
Content_Starting_Index = row[headers["Content Starting Index"]]
50+
Node_Name = row[headers["Node Name"]]
51+
Node_Type = row[headers["Node Type"]]
52+
Kubernetes_Cluster_Name = row[headers["Kubernetes Cluster Name"]]
53+
Masked = row[headers["Masked"]]
54+
description += f"**Filename:** {Filename}\n"
55+
description += f"**Rule:** {Rule}\n"
56+
description += f"**Node Name:** {Node_Name}\n"
57+
description += f"**Node Type:** {Node_Type}\n"
58+
description += f"**Kubernetes Cluster Name:** {Kubernetes_Cluster_Name}\n"
59+
description += f"**Content:** {Content}\n"
60+
description += f"**Content Starting Index:** {Content_Starting_Index}\n"
61+
description += f"**Masked:** {Masked}\n"
62+
title = f"{Rule} in {Filename}" if Rule else "Secret Finding"
63+
if Severity:
64+
return Finding(
65+
title=title,
66+
description=description,
67+
file_path=Filename,
68+
severity=self.severity(Severity),
69+
static_finding=False,
70+
dynamic_finding=True,
71+
test=test,
3372
)
34-
else:
35-
finding = None
36-
return finding
73+
return None
3774

3875
def severity(self, severity_input):
3976
if severity_input is None:

0 commit comments

Comments
 (0)