Docker Publish (Security Updates) #15
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Docker Publish (Security Updates) | |
on: | |
workflow_dispatch: | |
inputs: | |
force_build: | |
description: 'Force build even if no vulnerabilities found' | |
type: boolean | |
default: false | |
skip_scan: | |
description: 'Skip vulnerability scanning (for testing)' | |
type: boolean | |
default: false | |
schedule: | |
- cron: '0 0 * * *' # Daily at midnight UTC | |
permissions: | |
contents: write | |
jobs: | |
scan-vulnerabilities: | |
runs-on: ubuntu-24.04 | |
outputs: | |
has_vulnerabilities: ${{ steps.scan.outputs.has_vulnerabilities || inputs.force_build }} | |
steps: | |
# Single scan for both vulnerabilities and dependencies | |
- id: scan | |
if: inputs.skip_scan != true | |
uses: aquasecurity/trivy-action@0.29.0 | |
with: | |
image-ref: 'ghcr.io/serversideup/docker-ssh' | |
format: 'json' | |
output: 'trivy-results.json' | |
github-pat: ${{ secrets.GITHUB_TOKEN }} | |
ignore-unfixed: true | |
severity: 'CRITICAL,HIGH' | |
hide-progress: true | |
- name: Upload trivy report as a Github artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: trivy-results-json | |
path: '${{ github.workspace }}/trivy-results.json' | |
retention-days: 20 | |
# Parse results to set has_vulnerabilities (for workflow control) | |
- if: inputs.skip_scan != true | |
id: parse | |
shell: bash | |
run: | | |
if [ -f trivy-results.json ]; then | |
# Count both vulnerabilities and secrets | |
VULN_COUNT=$(jq -r '[.Results[] | (.Vulnerabilities, .Secrets) | select(. != null) | length] | add // 0' trivy-results.json) | |
if [ "${VULN_COUNT:-0}" -gt 0 ]; then | |
echo "has_vulnerabilities='true'" >> "$GITHUB_OUTPUT" | |
echo "# Security Findings Found" >> $GITHUB_STEP_SUMMARY | |
# Handle OS/Package Vulnerabilities | |
if jq -e '.Results[] | select(.Vulnerabilities != null)' trivy-results.json > /dev/null; then | |
echo "## Package Vulnerabilities" >> $GITHUB_STEP_SUMMARY | |
echo "| Severity | Package | Installed Version | Fixed Version | Vulnerability ID |" >> $GITHUB_STEP_SUMMARY | |
echo "|----------|---------|-------------------|---------------|-----------------|" >> $GITHUB_STEP_SUMMARY | |
jq -r '.Results[] | select(.Vulnerabilities != null) | .Vulnerabilities[] | "| \(.Severity) | \(.PkgName) | \(.InstalledVersion) | \(.FixedVersion) | \(.VulnerabilityID) |"' trivy-results.json >> $GITHUB_STEP_SUMMARY | |
fi | |
# Handle Secrets | |
if jq -e '.Results[] | select(.Secrets != null)' trivy-results.json > /dev/null; then | |
echo "## Secrets" >> $GITHUB_STEP_SUMMARY | |
echo "| Severity | Category | Title | Target | Rule ID |" >> $GITHUB_STEP_SUMMARY | |
echo "|----------|-----------|--------|---------|----------|" >> $GITHUB_STEP_SUMMARY | |
jq -r '.Results[] | select(.Secrets != null) | .Secrets[] | "| \(.Severity) | \(.Category) | \(.Title) | \(.Target) | \(.RuleID) |"' trivy-results.json >> $GITHUB_STEP_SUMMARY | |
fi | |
echo "::notice::Found ${VULN_COUNT} security findings that need to be addressed." | |
else | |
echo "has_vulnerabilities='false'" >> "$GITHUB_OUTPUT" | |
echo "No security findings found." >> $GITHUB_STEP_SUMMARY | |
fi | |
else | |
echo "has_vulnerabilities='false'" >> "$GITHUB_OUTPUT" | |
echo "::error::trivy-results.json not found" | |
exit 1 | |
fi | |
get-latest-release: | |
runs-on: ubuntu-24.04 | |
outputs: | |
release_version: ${{ steps.get-version.outputs.release_version }} | |
steps: | |
- name: Get Latest Release | |
id: get-version | |
run: | | |
LATEST_RELEASE=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name) | |
echo "release_version=${LATEST_RELEASE}" >> "$GITHUB_OUTPUT" | |
build-security-updates: | |
needs: [scan-vulnerabilities, get-latest-release] | |
if: needs.scan-vulnerabilities.outputs.has_vulnerabilities == 'true' || inputs.force_build == true | |
uses: ./.github/workflows/service_docker-build-and-publish.yml | |
secrets: inherit | |
with: | |
release_type: 'security' | |
ref_type: 'tag' | |
version: "${{ needs.get-latest-release.outputs.release_version }}" | |
notify: | |
needs: [build-security-updates] | |
runs-on: ubuntu-24.04 | |
if: always() | |
steps: | |
- name: Notify maintainers privately | |
if: needs.build-security-updates.result == 'success' | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
await github.rest.securityAdvisories.createPrivateVulnerabilityReport({ | |
owner: context.repo.owner, | |
repo: context.repo.name, | |
title: 'Automated Security Updates Applied', | |
description: `Security updates were automatically applied.\n\nAction Run: ${context.serverUrl}/${context.repo.owner}/${context.repo.name}/actions/runs/${context.runId}`, | |
state: 'closed', | |
severity: 'low', | |
identifiers: [{ | |
type: 'GHSA', | |
value: `GHSA-auto-${context.runId}` | |
}] | |
}); |