Skip to content

Commit 81e8162

Browse files
authored
Update vulnerability-scan.yml
1 parent 8ee22cd commit 81e8162

1 file changed

Lines changed: 97 additions & 112 deletions

File tree

.github/workflows/vulnerability-scan.yml

Lines changed: 97 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,14 @@ jobs:
2020
creds: '{"clientId":"${{ secrets.EPPLUS_CODE_SIGNING_APPLICATION_ID }}","clientSecret":"${{ secrets.EPPLUS_CODE_SIGNING_SECRET }}","subscriptionId":"${{ secrets.EPPLUS_CODE_SIGNING_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.EPPLUS_CODE_SIGNING_TENENT_ID }}"}'
2121

2222
- name: Install grype
23-
run: curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.110.0
24-
25-
- name: Install syft
26-
run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin v1.21.0
23+
run: curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
2724

2825
- name: Fetch SBOM index
2926
run: |
3027
curl -sSf https://epplussoftware.com/security/sbom/all.json -o all.json
3128
echo "SBOM index fetched:"
3229
cat all.json
3330
34-
- name: Start scan
35-
shell: bash
36-
run: |
37-
SCAN_ID=$(uuidgen)
38-
echo "SCAN_ID=$SCAN_ID" >> $GITHUB_ENV
39-
40-
GRYPE_VERSION=$(grype version | grep '^Version' | awk '{print $2}')
41-
42-
response=$(curl -s -o response.json -w "%{http_code}" \
43-
-X POST "https://epplussoftware.com/api/security/vulnerability/scan/start?scanId=${SCAN_ID}&grypeVersion=${GRYPE_VERSION}" \
44-
-H "X-Api-Key: ${{ secrets.EPPLUS_VULNERABILITY_API_KEY }}")
45-
46-
if [ "$response" != "200" ]; then
47-
echo "ERROR: Failed to start scan with HTTP $response"
48-
cat response.json
49-
exit 1
50-
fi
51-
52-
SCAN_DB_ID=$(jq -r '.scanDbId' response.json)
53-
echo "SCAN_DB_ID=$SCAN_DB_ID" >> $GITHUB_ENV
54-
55-
echo "Scan started with ID $SCAN_ID (db id: $SCAN_DB_ID)"
56-
cat response.json
57-
5831
- name: Scan each SBOM
5932
shell: bash
6033
run: |
@@ -64,53 +37,83 @@ jobs:
6437
for row in $versions; do
6538
entry=$(echo "$row" | base64 --decode)
6639
version=$(echo "$entry" | jq -r '.version')
67-
echo "--- Processing EPPlus $version ---"
68-
69-
# Scan per-TFM SBOMs
70-
tfms=$(echo "$entry" | jq -r '.targetFrameworks[]? | @base64')
71-
for tfm_row in $tfms; do
72-
tfm_entry=$(echo "$tfm_row" | base64 --decode)
73-
tfm=$(echo "$tfm_entry" | jq -r '.tfm')
74-
expected_sha256=$(echo "$tfm_entry" | jq -r '.sha256' | tr -d '\xEF\xBB\xBF' | tr -d '[:space:]')
75-
76-
echo " -> TFM: $tfm"
77-
78-
# Download per-TFM SBOM
79-
sbom_file="epplus-${version}.${tfm}.sbom.json"
80-
curl -sSf "https://epplussoftware.com/security/sbom/${version}/${tfm}.json" -o "$sbom_file"
81-
82-
# Validate checksum
83-
actual_sha256=$(sha256sum "$sbom_file" | awk '{ print $1 }')
84-
if [ "$actual_sha256" != "$expected_sha256" ]; then
85-
echo "ERROR: Checksum mismatch for EPPlus $version / $tfm"
86-
echo " Expected: $expected_sha256"
87-
echo " Actual: $actual_sha256"
88-
exit 1
89-
fi
90-
echo " Checksum OK for $version / $tfm"
40+
expected_sha256=$(echo "$entry" | jq -r '.sha256' | tr -d '\xEF\xBB\xBF' | tr -d '[:space:]')
9141
92-
# Convert CycloneDX SBOM to Syft JSON format
93-
syft_file="epplus-${version}.${tfm}.syft.json"
94-
syft scan "file:./${sbom_file}" -o "syft-json=./${syft_file}"
42+
echo "--- Processing EPPlus $version ---"
9543
96-
# Run grype scan
97-
mkdir -p "./reports/${version}/${tfm}"
98-
grype --add-cpes-if-none "sbom:./${syft_file}" --output json --file "./reports/${version}/${tfm}/report.json"
99-
echo " Scan complete for $version / $tfm"
100-
done
44+
# Download combined SBOM directly from Azure Blob Storage to avoid web cache
45+
sbom_file="epplus-${version}.sbom.json"
46+
az storage blob download \
47+
--account-name eppluswebprod \
48+
--container-name sbom \
49+
--name "$sbom_file" \
50+
--file "$sbom_file" \
51+
--auth-mode login
52+
53+
# Validate checksum
54+
actual_sha256=$(sha256sum "$sbom_file" | awk '{ print $1 }')
55+
if [ "$actual_sha256" != "$expected_sha256" ]; then
56+
echo "ERROR: Checksum mismatch for EPPlus $version"
57+
echo " Expected: $expected_sha256"
58+
echo " Actual: $actual_sha256"
59+
exit 1
60+
fi
61+
echo "Checksum OK for EPPlus $version"
62+
63+
# Scan per-TFM SBOMs if available, otherwise fall back to combined SBOM
64+
tfm_entries=$(echo "$entry" | jq -r '.targetFrameworks // [] | .[] | @base64')
65+
if [ -n "$tfm_entries" ]; then
66+
for tfm_row in $tfm_entries; do
67+
tfm_entry=$(echo "$tfm_row" | base64 --decode)
68+
tfm=$(echo "$tfm_entry" | jq -r '.tfm')
69+
expected_tfm_sha256=$(echo "$tfm_entry" | jq -r '.sha256' | tr -d '\xEF\xBB\xBF' | tr -d '[:space:]')
70+
71+
echo " Scanning TFM: $tfm"
72+
73+
# Download per-TFM SBOM directly from Azure Blob Storage
74+
tfm_sbom_file="epplus-${version}.${tfm}.sbom.json"
75+
az storage blob download \
76+
--account-name eppluswebprod \
77+
--container-name sbom \
78+
--name "$tfm_sbom_file" \
79+
--file "$tfm_sbom_file" \
80+
--auth-mode login
81+
82+
# Validate checksum
83+
actual_tfm_sha256=$(sha256sum "$tfm_sbom_file" | awk '{ print $1 }')
84+
if [ "$actual_tfm_sha256" != "$expected_tfm_sha256" ]; then
85+
echo "ERROR: Checksum mismatch for EPPlus $version / $tfm"
86+
echo " Expected: $expected_tfm_sha256"
87+
echo " Actual: $actual_tfm_sha256"
88+
exit 1
89+
fi
90+
echo " Checksum OK for EPPlus $version / $tfm"
91+
92+
# Run grype directly against CycloneDX SBOM
93+
mkdir -p "./reports/${version}/${tfm}"
94+
grype --add-cpes-if-none "sbom:./${tfm_sbom_file}" --output json --file "./reports/${version}/${tfm}/report.json"
95+
echo " Scan complete for EPPlus $version / $tfm"
96+
done
97+
else
98+
# No per-TFM SBOMs — scan combined SBOM
99+
echo " No per-TFM SBOMs found, scanning combined SBOM"
100+
mkdir -p "./reports/${version}"
101+
grype --add-cpes-if-none "sbom:./${sbom_file}" --output json --file "./reports/${version}/report.json"
102+
echo " Scan complete for EPPlus $version (combined)"
103+
fi
101104
done
102105
103106
- name: Upload reports to Azure Blob Storage
104107
shell: bash
105108
run: |
106-
for report in ./reports/*/*/report.json; do
107-
version=$(echo "$report" | sed 's|./reports/||' | cut -d'/' -f1)
108-
tfm=$(echo "$report" | sed 's|./reports/||' | cut -d'/' -f2)
109-
echo "Uploading report for EPPlus $version / $tfm"
109+
find ./reports -name "report.json" | while read report; do
110+
# Strip leading ./reports/ to get the blob name
111+
blob_name="${report#./reports/}"
112+
echo "Uploading $blob_name"
110113
az storage blob upload \
111114
--account-name eppluswebprod \
112115
--container-name vulnerability-reports \
113-
--name "${version}/${tfm}/report.json" \
116+
--name "$blob_name" \
114117
--file "$report" \
115118
--auth-mode login \
116119
--overwrite
@@ -124,61 +127,43 @@ jobs:
124127
entry=$(echo "$row" | base64 --decode)
125128
version=$(echo "$entry" | jq -r '.version')
126129
127-
tfms=$(echo "$entry" | jq -r '.targetFrameworks[]? | @base64')
128-
for tfm_row in $tfms; do
129-
tfm_entry=$(echo "$tfm_row" | base64 --decode)
130-
tfm=$(echo "$tfm_entry" | jq -r '.tfm')
131-
132-
echo "--- Indexing EPPlus $version / $tfm ---"
130+
tfm_entries=$(echo "$entry" | jq -r '.targetFrameworks // [] | .[] | @base64')
131+
if [ -n "$tfm_entries" ]; then
132+
for tfm_row in $tfm_entries; do
133+
tfm_entry=$(echo "$tfm_row" | base64 --decode)
134+
tfm=$(echo "$tfm_entry" | jq -r '.tfm')
135+
136+
echo "--- Indexing EPPlus $version / $tfm ---"
137+
response=$(curl -s -o response.json -w "%{http_code}" \
138+
-X POST "https://epplussoftware.com/api/security/vulnerability/index/${version}?tfm=${tfm}" \
139+
-H "X-Api-Key: ${{ secrets.EPPLUS_VULNERABILITY_API_KEY }}" \
140+
-H "Content-Type: application/json" \
141+
-d @"./reports/${version}/${tfm}/report.json")
142+
143+
if [ "$response" != "200" ]; then
144+
echo "ERROR: Indexing failed for EPPlus $version / $tfm with HTTP $response"
145+
cat response.json
146+
exit 1
147+
fi
148+
149+
echo "Indexed EPPlus $version / $tfm successfully"
150+
cat response.json
151+
done
152+
else
153+
echo "--- Indexing EPPlus $version (combined) ---"
133154
response=$(curl -s -o response.json -w "%{http_code}" \
134-
-X POST "https://epplussoftware.com/api/security/vulnerability/index/${version}?tfm=${tfm}&scanId=${SCAN_DB_ID}" \
155+
-X POST "https://epplussoftware.com/api/security/vulnerability/index/${version}" \
135156
-H "X-Api-Key: ${{ secrets.EPPLUS_VULNERABILITY_API_KEY }}" \
136157
-H "Content-Type: application/json" \
137-
-d @"./reports/${version}/${tfm}/report.json")
158+
-d @"./reports/${version}/report.json")
138159
139160
if [ "$response" != "200" ]; then
140-
echo "ERROR: Indexing failed for EPPlus $version / $tfm with HTTP $response"
161+
echo "ERROR: Indexing failed for EPPlus $version with HTTP $response"
141162
cat response.json
142163
exit 1
143164
fi
144165
145-
echo "Indexed EPPlus $version / $tfm successfully"
166+
echo "Indexed EPPlus $version successfully"
146167
cat response.json
147-
done
148-
done
149-
150-
- name: Complete scan
151-
shell: bash
152-
if: success()
153-
run: |
154-
response=$(curl -s -o response.json -w "%{http_code}" \
155-
-X POST "https://epplussoftware.com/api/security/vulnerability/scan/complete?scanId=${SCAN_ID}&status=completed" \
156-
-H "X-Api-Key: ${{ secrets.EPPLUS_VULNERABILITY_API_KEY }}")
157-
158-
if [ "$response" != "200" ]; then
159-
echo "ERROR: Failed to complete scan with HTTP $response"
160-
cat response.json
161-
exit 1
162-
fi
163-
164-
echo "Scan $SCAN_ID completed successfully"
165-
cat response.json
166-
167-
- name: Fail scan
168-
shell: bash
169-
if: failure()
170-
run: |
171-
if [ -n "$SCAN_ID" ]; then
172-
response=$(curl -s -o response.json -w "%{http_code}" \
173-
-X POST "https://epplussoftware.com/api/security/vulnerability/scan/complete?scanId=${SCAN_ID}&status=failed" \
174-
-H "X-Api-Key: ${{ secrets.EPPLUS_VULNERABILITY_API_KEY }}")
175-
176-
if [ "$response" != "200" ]; then
177-
echo "WARNING: Failed to mark scan as failed with HTTP $response"
178-
cat response.json
179-
else
180-
echo "Scan $SCAN_ID marked as failed"
181168
fi
182-
else
183-
echo "No SCAN_ID available, skipping fail notification"
184-
fi
169+
done

0 commit comments

Comments
 (0)