Skip to content

Commit 0325368

Browse files
committed
Add a check for CVEs present in new transitive dependencies
This action will compare the baseline and PR's CVE count, and fail if new high or critical CVEs are present in the PR.
1 parent de0fe1c commit 0325368

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: 'Maven OWASP Dependency Check Scan'
2+
description: 'Runs OWASP dependency check Maven scan with consistent settings'
3+
inputs:
4+
working-directory:
5+
description: 'Working directory for Maven command'
6+
required: false
7+
default: '.'
8+
owasp-version:
9+
description: 'OWASP dependency check plugin version'
10+
required: false
11+
default: '12.1.3'
12+
data-directory:
13+
description: 'OWASP data directory path'
14+
required: false
15+
default: '$HOME/.owasp/dependency-check-data'
16+
runs:
17+
using: 'composite'
18+
steps:
19+
- name: Run OWASP dependency check
20+
working-directory: ${{ inputs.working-directory }}
21+
shell: bash
22+
run: |
23+
mvn org.owasp:dependency-check-maven:${{ inputs.owasp-version }}:aggregate \
24+
-DskipTests \
25+
-Dformat=JSON \
26+
-DprettyPrint=true \
27+
-DfailOnError=false \
28+
-DossindexAnalyzerEnabled=true \
29+
-DnvdApiAnalyzerEnabled=false \
30+
-DnodeAnalyzerEnabled=false \
31+
-DassemblyAnalyzerEnabled=false \
32+
-DcentralAnalyzerEnabled=false \
33+
-DnuspecAnalyzerEnabled=false \
34+
-DnvdValidForHours=168 \
35+
-DdataDirectory=${{ inputs.data-directory }}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: Maven OWASP Dependency Check
2+
permissions:
3+
contents: read
4+
on:
5+
pull_request:
6+
workflow_dispatch:
7+
inputs:
8+
cvss-threshold:
9+
description: 'CVSS score threshold for failing (7.0 = high/critical)'
10+
required: false
11+
default: '7.0'
12+
type: string
13+
14+
jobs:
15+
dependency-check:
16+
runs-on: ubuntu-latest
17+
env:
18+
CVSS_THRESHOLD: ${{ github.event.inputs.cvss-threshold || '7.0' }}
19+
OWASP_VERSION: '12.1.3'
20+
steps:
21+
# Checkout PR branch first to get access to the composite action
22+
- name: Checkout PR branch
23+
uses: actions/checkout@v4
24+
with:
25+
ref: ${{ github.event.pull_request.head.sha }}
26+
27+
- name: Checkout base branch
28+
uses: actions/checkout@v4
29+
with:
30+
ref: ${{ github.event.pull_request.base.sha }}
31+
path: base
32+
33+
- name: Set up Java
34+
uses: actions/setup-java@v4
35+
with:
36+
distribution: 'temurin'
37+
java-version: 17
38+
cache: 'maven'
39+
40+
- name: Get date for cache key
41+
id: get-date
42+
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
43+
44+
- name: Restore OWASP database cache
45+
uses: actions/cache/restore@v4
46+
id: cache-owasp-restore
47+
with:
48+
path: ~/.owasp/dependency-check-data
49+
key: owasp-cache-${{ runner.os }}-v${{ env.OWASP_VERSION }}-${{ steps.get-date.outputs.date }}
50+
restore-keys: |
51+
owasp-cache-${{ runner.os }}-v${{ env.OWASP_VERSION }}-
52+
owasp-cache-${{ runner.os }}-
53+
54+
- name: Run OWASP check on base branch
55+
uses: ./.github/actions/maven-owasp-scan
56+
with:
57+
working-directory: base
58+
owasp-version: ${{ env.OWASP_VERSION }}
59+
data-directory: $HOME/.owasp/dependency-check-data
60+
61+
- name: Save OWASP cache after base scan
62+
if: steps.cache-owasp-restore.outputs.cache-hit != 'true'
63+
uses: actions/cache/save@v4
64+
with:
65+
path: ~/.owasp/dependency-check-data
66+
key: owasp-cache-${{ runner.os }}-v${{ env.OWASP_VERSION }}-${{ steps.get-date.outputs.date }}-partial
67+
68+
- name: Run OWASP check on PR branch
69+
uses: ./.github/actions/maven-owasp-scan
70+
with:
71+
working-directory: .
72+
owasp-version: ${{ env.OWASP_VERSION }}
73+
data-directory: $HOME/.owasp/dependency-check-data
74+
75+
- name: Compare and fail on new CVEs above threshold
76+
run: |
77+
# Extract CVEs above threshold from both branches (CVSS >= $CVSS_THRESHOLD)
78+
threshold="${{ env.CVSS_THRESHOLD }}"
79+
80+
# Validate report files exist
81+
if [ ! -f base/target/dependency-check-report.json ]; then
82+
echo "❌ Missing base report: base/target/dependency-check-report.json"
83+
exit 1
84+
fi
85+
if [ ! -f target/dependency-check-report.json ]; then
86+
echo "❌ Missing PR report: target/dependency-check-report.json"
87+
exit 1
88+
fi
89+
90+
# Validate report files are valid JSON
91+
if ! jq empty base/target/dependency-check-report.json >/dev/null 2>&1; then
92+
echo "❌ Malformed JSON in base/target/dependency-check-report.json"
93+
exit 1
94+
fi
95+
if ! jq empty target/dependency-check-report.json >/dev/null 2>&1; then
96+
echo "❌ Malformed JSON in target/dependency-check-report.json"
97+
exit 1
98+
fi
99+
100+
base_cves=$(jq -r ".dependencies[].vulnerabilities[]? | select((.cvssv2.score // 0) >= $threshold or (.cvssv3.baseScore // 0) >= $threshold) | .name" base/target/dependency-check-report.json | grep -E '^CVE-[0-9]{4}-[0-9]+$' | sort -u)
101+
pr_cves=$(jq -r ".dependencies[].vulnerabilities[]? | select((.cvssv2.score // 0) >= $threshold or (.cvssv3.baseScore // 0) >= $threshold) | .name" target/dependency-check-report.json | grep -E '^CVE-[0-9]{4}-[0-9]+$' | sort -u)
102+
103+
# Find new CVEs introduced in PR
104+
new_cves=$(comm -13 <(echo "$base_cves") <(echo "$pr_cves"))
105+
106+
if [ -n "$new_cves" ]; then
107+
echo "❌ New vulnerabilities with CVSS >= $threshold introduced in PR:"
108+
echo "$new_cves"
109+
echo ""
110+
111+
for cve in $new_cves; do
112+
echo "=================================================="
113+
echo "CVE: $cve"
114+
echo "=================================================="
115+
116+
# Find which dependencies have this CVE
117+
jq -r '
118+
.dependencies[]
119+
| select(.vulnerabilities[]?.name == "'"$cve"'")
120+
| "Module: " + (.projectReferences // ["root"])[0]
121+
+ "\nDependency: " + .fileName
122+
+ "\nPackage: " + (if .packages and .packages[0] then .packages[0].id else "N/A" end)
123+
+ "\nDescription: " + (
124+
[.vulnerabilities[] | select(.name == "'"$cve"'") | .description]
125+
| unique
126+
| join("\nDescription: ")
127+
)
128+
' target/dependency-check-report.json
129+
130+
echo ""
131+
done
132+
133+
exit 1
134+
else
135+
echo "✅ No new vulnerabilities introduced"
136+
fi
137+
138+
- name: Save OWASP database cache
139+
if: always()
140+
uses: actions/cache/save@v4
141+
with:
142+
path: ~/.owasp/dependency-check-data
143+
key: owasp-cache-${{ runner.os }}-v${{ env.OWASP_VERSION }}-${{ steps.get-date.outputs.date }}
144+
145+
- name: Upload reports
146+
if: always()
147+
uses: actions/upload-artifact@v4
148+
with:
149+
name: owasp-reports
150+
path: |
151+
base/target/dependency-check-report.json
152+
target/dependency-check-report.json

0 commit comments

Comments
 (0)