name: Trivy Security Scan (Template) on: workflow_call: inputs: images: description: 'JSON array of images to build and scan. Each entry: {"name": "my-app", "dockerfile": "Dockerfile", "context": "."}' required: false type: string default: '[]' secrets: GITHUB_TOKEN: required: true jobs: trivy-scan: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Trivy run: | curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin - name: Trivy filesystem scan run: | trivy fs --no-progress --severity HIGH,CRITICAL \ --format json . \ > /tmp/fs-scan.json || true - name: Build and scan Docker images run: | IMAGES='${{ inputs.images }}' echo "$IMAGES" | jq -c '.[]' | while read -r item; do NAME=$(echo "$item" | jq -r '.name') DOCKERFILE=$(echo "$item" | jq -r '.dockerfile') CONTEXT=$(echo "$item" | jq -r '.context') echo "Building image: ${NAME}" docker build -t "${NAME}:latest" -f "${DOCKERFILE}" "${CONTEXT}" echo "Scanning image: ${NAME}" trivy image --no-progress --severity HIGH,CRITICAL \ --format json "${NAME}:latest" \ > "/tmp/image-scan-${NAME}.json" || true done - name: Generate and publish security report to wiki env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | SCAN_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC') GITEA_URL="${{ github.server_url }}" REPO="${{ github.repository }}" COMMIT_SHA="${{ github.sha }}" COMMIT_SHORT=$(echo "$COMMIT_SHA" | cut -c1-7) generate_report() { local json_file="$1" local title="$2" if ! jq empty "$json_file" 2>/dev/null; then echo "## ${title}" echo "" echo "Failed to parse scan results." echo "" return fi local total_vulns total_vulns=$(jq '[.Results[]? | .Vulnerabilities[]?] | length' "$json_file") local high_count high_count=$(jq '[.Results[]? | .Vulnerabilities[]? | select(.Severity == "HIGH")] | length' "$json_file") local critical_count critical_count=$(jq '[.Results[]? | .Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' "$json_file") echo "## ${title}" echo "" echo "| Total | CRITICAL | HIGH |" echo "|-------|----------|------|" echo "| ${total_vulns} | ${critical_count} | ${high_count} |" echo "" if [ "$total_vulns" -eq 0 ]; then echo "No vulnerabilities found." echo "" return fi echo "| Package | Installed | Fixed | Vulnerability | Severity | Title |" echo "|---------|-----------|-------|---------------|----------|-------|" jq -r ' [.Results[]? | .Vulnerabilities[]?] | sort_by(if .Severity == "CRITICAL" then 0 else 1 end) | .[] | "| \(.PkgName) | \(.InstalledVersion) | \(.FixedVersion // "-") | \(.VulnerabilityID) | \(.Severity) | \(.Title // "-" | gsub("[\\n\\r]"; " ") | if length > 60 then .[:60] + "..." else . end) |" ' "$json_file" echo "" } { echo "# Security Scan Report" echo "" echo "| | |" echo "|---|---|" echo "| **Date** | ${SCAN_DATE} |" echo "| **Commit** | ${COMMIT_SHORT} |" echo "| **Branch** | ${{ github.ref_name }} |" echo "| **Severity Filter** | HIGH, CRITICAL |" echo "" echo "---" echo "" generate_report /tmp/fs-scan.json "Filesystem Scan" IMAGES='${{ inputs.images }}' echo "$IMAGES" | jq -c '.[]' | while read -r item; do NAME=$(echo "$item" | jq -r '.name') echo "---" echo "" generate_report "/tmp/image-scan-${NAME}.json" "Docker Image - ${NAME}" done } > /tmp/wiki-content.md base64 -w 0 /tmp/wiki-content.md > /tmp/content_b64.txt HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: token ${GITHUB_TOKEN}" \ "${GITEA_URL}/api/v1/repos/${REPO}/wiki/page/security") if [ "$HTTP_CODE" = "200" ]; then jq -n \ --rawfile content /tmp/content_b64.txt \ --arg message "Update security scan report - ${SCAN_DATE}" \ --arg title "security" \ '{content_base64: ($content | rtrimstr("\n")), message: $message, title: $title}' > /tmp/payload.json curl -s -X PATCH \ -H "Authorization: token ${GITHUB_TOKEN}" \ -H "Content-Type: application/json" \ -d @/tmp/payload.json \ "${GITEA_URL}/api/v1/repos/${REPO}/wiki/page/security" echo "Wiki page 'security' updated." else jq -n \ --rawfile content /tmp/content_b64.txt \ --arg message "Create security scan report - ${SCAN_DATE}" \ --arg title "security" \ '{content_base64: ($content | rtrimstr("\n")), message: $message, title: $title}' > /tmp/payload.json curl -s -X POST \ -H "Authorization: token ${GITHUB_TOKEN}" \ -H "Content-Type: application/json" \ -d @/tmp/payload.json \ "${GITEA_URL}/api/v1/repos/${REPO}/wiki/new" echo "Wiki page 'security' created." fi