also looking at this
fix(security): validate vulnerability scans and pin scanner downloads
#300
5 files
+192
-56
| @@ -33,8 +33,13 @@ jobs: | ||
| 33 | 33 | |
| 34 | 34 | - name: Install SBOM tools |
| 35 | 35 | run: | |
| 36 | # Install Syft for comprehensive SBOM (works with bun.lock) | |
| 37 | curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin | |
| 36 | set -euo pipefail | |
| 37 | mkdir -p "$RUNNER_TEMP/syft" | |
| 38 | cd "$RUNNER_TEMP/syft" | |
| 39 | curl --fail --location --retry 3 --output syft.tar.gz https://github.com/anchore/syft/releases/download/v1.52.0/syft_1.52.0_linux_amd64.tar.gz | |
| 40 | echo 'caeedb81fb0491615f1ebd1761e4145d41ee86dd2cc7bf80669f9f5ad9d6133d syft.tar.gz' | sha256sum --check --strict | |
| 41 | tar -xzf syft.tar.gz syft | |
| 42 | echo "$RUNNER_TEMP/syft" >> "$GITHUB_PATH" | |
| 38 | 43 | |
| 39 | 44 | - name: Generate comprehensive SBOM (Syft) |
| 40 | 45 | run: | |
| @@ -228,11 +233,13 @@ jobs: | ||
| 228 | 233 | output-file: sbom/github-sbom.json |
| 229 | 234 | |
| 230 | 235 | vulnerability-scan: |
| 231 | # name: SBOM Vulnerability Scan | |
| 232 | 236 | needs: generate |
| 233 | 237 | runs-on: ubuntu-latest |
| 234 | 238 | |
| 235 | 239 | steps: |
| 240 | - uses: actions/checkout@v6 | |
| 241 | - uses: oven-sh/setup-bun@v2 | |
| 242 | ||
| 236 | 243 | - name: Download SBOM |
| 237 | 244 | uses: actions/download-artifact@v4 |
| 238 | 245 | with: |
| @@ -241,64 +248,22 @@ jobs: | ||
| 241 | 248 | |
| 242 | 249 | - name: Install Grype |
| 243 | 250 | run: | |
| 244 | curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin | |
| 251 | set -euo pipefail | |
| 252 | mkdir -p "$RUNNER_TEMP/grype" | |
| 253 | cd "$RUNNER_TEMP/grype" | |
| 254 | curl --fail --location --retry 3 --output grype.tar.gz https://github.com/anchore/grype/releases/download/v0.119.0/grype_0.119.0_linux_amd64.tar.gz | |
| 255 | echo '3fa2dc4b924621ab65404cf08d0b8438d896d80ab949c9d5a4ca283c36004c9b grype.tar.gz' | sha256sum --check --strict | |
| 256 | tar -xzf grype.tar.gz grype | |
| 257 | echo "$RUNNER_TEMP/grype" >> "$GITHUB_PATH" | |
| 245 | 258 | |
| 246 | - name: Scan SBOM for vulnerabilities | |
| 247 | id: scan | |
| 248 | run: | | |
| 249 | mkdir -p scan-results | |
| 250 | ||
| 251 | # Scan the full SBOM | |
| 252 | if [ -f "sbom/full-sbom.cyclonedx.json" ]; then | |
| 253 | grype sbom:sbom/full-sbom.cyclonedx.json \ | |
| 254 | --output json \ | |
| 255 | --file scan-results/vulnerabilities.json \ | |
| 256 | || true | |
| 257 | ||
| 258 | grype sbom:sbom/full-sbom.cyclonedx.json \ | |
| 259 | --output table \ | |
| 260 | --file scan-results/vulnerabilities.txt \ | |
| 261 | || true | |
| 262 | fi | |
| 263 | ||
| 264 | # Count vulnerabilities by severity | |
| 265 | if [ -f "scan-results/vulnerabilities.json" ]; then | |
| 266 | CRITICAL=$(jq '[.matches[] | select(.vulnerability.severity == "Critical")] | length' scan-results/vulnerabilities.json 2>/dev/null || echo "0") | |
| 267 | HIGH=$(jq '[.matches[] | select(.vulnerability.severity == "High")] | length' scan-results/vulnerabilities.json 2>/dev/null || echo "0") | |
| 268 | MEDIUM=$(jq '[.matches[] | select(.vulnerability.severity == "Medium")] | length' scan-results/vulnerabilities.json 2>/dev/null || echo "0") | |
| 269 | LOW=$(jq '[.matches[] | select(.vulnerability.severity == "Low")] | length' scan-results/vulnerabilities.json 2>/dev/null || echo "0") | |
| 270 | ||
| 271 | echo "critical=$CRITICAL" >> $GITHUB_OUTPUT | |
| 272 | echo "high=$HIGH" >> $GITHUB_OUTPUT | |
| 273 | echo "medium=$MEDIUM" >> $GITHUB_OUTPUT | |
| 274 | echo "low=$LOW" >> $GITHUB_OUTPUT | |
| 275 | fi | |
| 276 | ||
| 277 | - name: Generate vulnerability report | |
| 278 | run: | | |
| 279 | echo "## Vulnerability Scan Results" >> $GITHUB_STEP_SUMMARY | |
| 280 | echo "" >> $GITHUB_STEP_SUMMARY | |
| 281 | echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY | |
| 282 | echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY | |
| 283 | echo "| Critical | $ |" >> $GITHUB_STEP_SUMMARY | |
| 284 | echo "| High | $ |" >> $GITHUB_STEP_SUMMARY | |
| 285 | echo "| Medium | $ |" >> $GITHUB_STEP_SUMMARY | |
| 286 | echo "| Low | $ |" >> $GITHUB_STEP_SUMMARY | |
| 287 | ||
| 288 | if [ "$" -gt 0 ] || [ "$" -gt 0 ]; then | |
| 289 | echo "" >> $GITHUB_STEP_SUMMARY | |
| 290 | echo "⚠️ **Action Required:** Critical or High severity vulnerabilities found!" >> $GITHUB_STEP_SUMMARY | |
| 291 | fi | |
| 259 | - name: Scan and validate vulnerability report | |
| 260 | run: bun scripts/scan-sbom.ts sbom/full-sbom.cyclonedx.json scan-results | |
| 292 | 261 | |
| 293 | 262 | - name: Upload scan results |
| 263 | if: ${{ always() }} | |
| 294 | 264 | uses: actions/upload-artifact@v4 |
| 295 | 265 | with: |
| 296 | 266 | name: vulnerability-scan |
| 297 | 267 | path: scan-results/ |
| 268 | if-no-files-found: warn | |
| 298 | 269 | retention-days: 90 |
| 299 | ||
| 300 | - name: Fail on critical vulnerabilities | |
| 301 | if: steps.scan.outputs.critical > 0 | |
| 302 | run: | | |
| 303 | echo "::error::Critical vulnerabilities found in dependencies!" | |
| 304 | exit 1 | |