[2317] Auth for http(s) proxy #1251
This file contains hidden or 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: Go Security scan | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| release: | |
| types: [published] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| setup: | |
| name: Shared Setup | |
| runs-on: ubuntu-latest | |
| outputs: | |
| go-version: '1.22' | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| - name: Export Go Version | |
| run: echo "go-version=1.22" >> $GITHUB_OUTPUT | |
| gosec_scan: | |
| name: Gosec Security Scan (Full) | |
| needs: setup | |
| runs-on: ubuntu-latest | |
| outputs: | |
| gosec_high_found: ${{ steps.scan.outputs.gosec_high_found }} | |
| gosec_current_count: ${{ steps.gosec_compare.outputs.current_count }} | |
| gosec_baseline_count: ${{ steps.gosec_compare.outputs.baseline_count }} | |
| gosec_added_count: ${{ steps.gosec_compare.outputs.added_count }} | |
| gosec_fixed_count: ${{ steps.gosec_compare.outputs.fixed_count }} | |
| gosec_baseline_method: ${{ steps.gosec_compare.outputs.baseline_method }} | |
| gosec_added_details: ${{ steps.gosec_compare.outputs.added_details }} | |
| gosec_fixed_details: ${{ steps.gosec_compare.outputs.fixed_details }} | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '${{ needs.setup.outputs.go-version }}' | |
| - name: Install gosec | |
| run: | | |
| go install github.com/securego/gosec/v2/cmd/gosec@latest | |
| echo "$(go env GOPATH)/bin" >> $GITHUB_PATH | |
| - name: Sanitize branch name | |
| run: echo "SAFE_REF_NAME=${GITHUB_REF_NAME//\//-}" >> $GITHUB_ENV | |
| - name: Run Gosec Scan | |
| id: scan | |
| run: | | |
| echo "Running Gosec scan..." | |
| mkdir -p tmp | |
| gosec -fmt=json -severity=high -out=tmp/gosec-report.json ./... || true | |
| cat tmp/gosec-report.json || echo '{"Issues":[]}' | |
| count=$(jq '[.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL")] | length' tmp/gosec-report.json || echo 0) | |
| if [[ "$count" -gt 0 ]]; then | |
| echo "gosec_high_found=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "gosec_high_found=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Upload Gosec Report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gosec-json-${{ env.SAFE_REF_NAME }} | |
| path: tmp/gosec-report.json | |
| - name: Generate PR Body (if vulnerabilities found) | |
| if: ${{ steps.scan.outputs.gosec_high_found == 'true' }} | |
| run: | | |
| echo "# 🚨 Gosec Vulnerability Report for branch \`${GITHUB_REF_NAME}\`" > tmp/pr-body.md | |
| jq -r ' | |
| .Issues[] | |
| | select(.severity == "HIGH" or .severity == "CRITICAL") | |
| | "* File: \(.file)\n • Line: \(.line)\n • Rule ID: \(.rule_id)\n • Details: \(.details)\n • Confidence: \(.confidence)\n • Severity: \(.severity)\n" | |
| ' tmp/gosec-report.json >> tmp/pr-body.md | |
| - name: Debug - List Available Artifacts | |
| if: github.event_name == 'pull_request' | |
| continue-on-error: true | |
| run: | | |
| echo "Looking for artifacts from base SHA: ${{ github.event.pull_request.base.sha }}" | |
| echo "Expected Gosec artifact: gosec-security-report-${{ github.event.pull_request.base.sha }}" | |
| echo "Base branch: ${{ github.event.pull_request.base.ref }}" | |
| - name: Download Baseline Gosec Report (PR only) | |
| if: github.event_name == 'pull_request' | |
| continue-on-error: true | |
| uses: dawidd6/action-download-artifact@v3 | |
| with: | |
| name: gosec-security-report-${{ github.event.pull_request.base.sha }} | |
| path: ./baseline-reports | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| workflow: security.yml | |
| branch: ${{ github.event.pull_request.base.ref }} | |
| workflow_conclusion: "" | |
| check_artifacts: true | |
| if_no_artifact_found: warn | |
| repo: ${{ github.repository }} | |
| search_artifacts: true | |
| - name: Compare Vulnerabilities with Baseline (PR only) | |
| if: github.event_name == 'pull_request' | |
| id: gosec_compare | |
| run: | | |
| echo "Comparing Gosec vulnerabilities with baseline..." | |
| # Initialize counts | |
| current_count=0 | |
| baseline_count=0 | |
| added_count=0 | |
| fixed_count=0 | |
| baseline_method="none" | |
| # Count current vulnerabilities | |
| if [[ -f "tmp/gosec-report.json" ]]; then | |
| current_count=$(jq '[.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL")] | length' tmp/gosec-report.json || echo 0) | |
| fi | |
| # Try to use artifact baseline first | |
| if [[ -f "baseline-reports/gosec-report.json" ]]; then | |
| echo "✅ Using artifact baseline from main branch" | |
| baseline_method="artifact" | |
| baseline_count=$(jq '[.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL")] | length' baseline-reports/gosec-report.json || echo 0) | |
| # Create unique vulnerability identifiers for comparison | |
| jq -r '.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL") | "\(.file):\(.line):\(.rule_id)"' tmp/gosec-report.json | sort > tmp/current-vulns.txt | |
| jq -r '.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL") | "\(.file):\(.line):\(.rule_id)"' baseline-reports/gosec-report.json | sort > tmp/baseline-vulns.txt | |
| # Calculate added and fixed vulnerabilities | |
| added_count=$(comm -23 tmp/current-vulns.txt tmp/baseline-vulns.txt | wc -l) | |
| fixed_count=$(comm -13 tmp/current-vulns.txt tmp/baseline-vulns.txt | wc -l) | |
| else | |
| echo "⚠️ No baseline artifact found, scanning base branch live..." | |
| baseline_method="live_scan" | |
| # Save current state | |
| cp tmp/gosec-report.json tmp/gosec-report-pr.json | |
| # Checkout and scan base branch | |
| git fetch origin ${{ github.event.pull_request.base.ref }} | |
| git checkout ${{ github.event.pull_request.base.sha }} | |
| # Run baseline Gosec scan | |
| gosec -fmt=json -severity=medium -out=tmp/gosec-report-baseline.json ./... || true | |
| cat tmp/gosec-report-baseline.json || echo '{"Issues":[]}' | |
| baseline_count=$(jq '[.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL")] | length' tmp/gosec-report-baseline.json || echo 0) | |
| # Create comparison files | |
| jq -r '.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL") | "\(.file):\(.line):\(.rule_id)"' tmp/gosec-report-pr.json | sort > tmp/current-vulns.txt | |
| jq -r '.Issues[] | select(.severity == "HIGH" or .severity == "CRITICAL") | "\(.file):\(.line):\(.rule_id)"' tmp/gosec-report-baseline.json | sort > tmp/baseline-vulns.txt | |
| # Calculate differences | |
| added_count=$(comm -23 tmp/current-vulns.txt tmp/baseline-vulns.txt | wc -l) | |
| fixed_count=$(comm -13 tmp/current-vulns.txt tmp/baseline-vulns.txt | wc -l) | |
| # Restore PR state | |
| git checkout ${{ github.sha }} | |
| cp tmp/gosec-report-pr.json tmp/gosec-report.json | |
| fi | |
| echo "Baseline method: $baseline_method" | |
| echo "Current HIGH/CRITICAL vulnerabilities: $current_count" | |
| echo "Baseline HIGH/CRITICAL vulnerabilities: $baseline_count" | |
| echo "Added vulnerabilities: $added_count" | |
| echo "Fixed vulnerabilities: $fixed_count" | |
| # Generate detailed vulnerability lists | |
| added_details="" | |
| fixed_details="" | |
| if [[ -f "tmp/current-vulns.txt" && -f "tmp/baseline-vulns.txt" ]]; then | |
| # Get added vulnerabilities with details | |
| if [[ $added_count -gt 0 ]]; then | |
| added_vulns=$(comm -23 tmp/current-vulns.txt tmp/baseline-vulns.txt) | |
| added_details="" | |
| while IFS= read -r vuln_id; do | |
| if [[ -n "$vuln_id" ]]; then | |
| file_path=$(echo "$vuln_id" | cut -d':' -f1) | |
| line_num=$(echo "$vuln_id" | cut -d':' -f2) | |
| rule_id=$(echo "$vuln_id" | cut -d':' -f3) | |
| details=$(jq -r --arg file "$file_path" --arg line "$line_num" --arg rule "$rule_id" ' | |
| .Issues[] | select(.file == $file and (.line | tostring) == $line and .rule_id == $rule and (.severity == "HIGH" or .severity == "CRITICAL")) | | |
| "**File:** `\(.file)` (Line \(.line))\\n**Rule:** \(.rule_id)\\n**Severity:** \(.severity)\\n**Details:** \(.details)\\n" | |
| ' tmp/gosec-report.json) | |
| if [[ -n "$details" ]]; then | |
| added_details="${added_details}${details}\\n" | |
| fi | |
| fi | |
| done <<< "$added_vulns" | |
| fi | |
| # Get fixed vulnerabilities with details | |
| if [[ $fixed_count -gt 0 ]]; then | |
| fixed_vulns=$(comm -13 tmp/current-vulns.txt tmp/baseline-vulns.txt) | |
| fixed_details="" | |
| while IFS= read -r vuln_id; do | |
| if [[ -n "$vuln_id" ]]; then | |
| file_path=$(echo "$vuln_id" | cut -d':' -f1) | |
| line_num=$(echo "$vuln_id" | cut -d':' -f2) | |
| rule_id=$(echo "$vuln_id" | cut -d':' -f3) | |
| baseline_report="baseline-reports/gosec-report.json" | |
| if [[ "$baseline_method" == "live_scan" ]]; then | |
| baseline_report="tmp/gosec-report-baseline.json" | |
| fi | |
| details=$(jq -r --arg file "$file_path" --arg line "$line_num" --arg rule "$rule_id" ' | |
| .Issues[] | select(.file == $file and (.line | tostring) == $line and .rule_id == $rule and (.severity == "HIGH" or .severity == "CRITICAL")) | | |
| "**File:** `\(.file)` (Line \(.line))\\n**Rule:** \(.rule_id)\\n**Severity:** \(.severity)\\n**Details:** \(.details)\\n" | |
| ' "$baseline_report") | |
| if [[ -n "$details" ]]; then | |
| fixed_details="${fixed_details}${details}\\n" | |
| fi | |
| fi | |
| done <<< "$fixed_vulns" | |
| fi | |
| fi | |
| # Set outputs | |
| echo "current_count=$current_count" >> "$GITHUB_OUTPUT" | |
| echo "baseline_count=$baseline_count" >> "$GITHUB_OUTPUT" | |
| echo "added_count=$added_count" >> "$GITHUB_OUTPUT" | |
| echo "fixed_count=$fixed_count" >> "$GITHUB_OUTPUT" | |
| echo "baseline_method=$baseline_method" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "added_details<<EOF" | |
| echo -e "$added_details" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| { | |
| echo "fixed_details<<EOF" | |
| echo -e "$fixed_details" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Display Gosec Vulnerability Report | |
| if: ${{ steps.scan.outputs.gosec_high_found == 'true' }} | |
| run: | | |
| echo "==========================================" | |
| echo "Gosec HIGH/CRITICAL Vulnerability Report" | |
| echo "==========================================" | |
| printf "%-60s %-6s %-8s %-10s %s\n" "FILE" "LINE" "RULE" "SEVERITY" "DETAILS" | |
| printf "%-60s %-6s %-8s %-10s %s\n" "----" "----" "----" "--------" "-------" | |
| jq -r ' | |
| .Issues[] | |
| | select(.severity == "HIGH" or .severity == "CRITICAL") | |
| | [.file, .line, .rule_id, .severity, .details] | |
| | @tsv | |
| ' tmp/gosec-report.json | while IFS=$'\t' read -r file line rule sev details; do | |
| printf "%-60s %-6s %-8s %-10s %s\n" "$file" "$line" "$rule" "$sev" "$details" | |
| done | |
| echo "==========================================" | |
| - name: Store Gosec Report as Artifact (Main/Release) | |
| if: ${{ (github.ref == 'refs/heads/main' && github.event_name == 'push') || github.event_name == 'release' }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gosec-security-report-${{ github.sha }} | |
| path: tmp/gosec-report.json | |
| retention-days: 90 | |
| - name: Save Gosec PR Summary for Comment Workflow | |
| if: github.event_name == 'pull_request' | |
| env: | |
| GOSEC_ADDED_DETAILS: ${{ steps.gosec_compare.outputs.added_details }} | |
| GOSEC_FIXED_DETAILS: ${{ steps.gosec_compare.outputs.fixed_details }} | |
| run: | | |
| mkdir -p tmp/gosec-summary | |
| echo "${{ github.event.pull_request.number }}" > tmp/gosec-summary/pr-number.txt | |
| echo "${{ github.event.pull_request.base.sha }}" > tmp/gosec-summary/base-sha.txt | |
| echo "${{ steps.gosec_compare.outputs.current_count }}" > tmp/gosec-summary/current.txt | |
| echo "${{ steps.gosec_compare.outputs.baseline_count }}" > tmp/gosec-summary/baseline.txt | |
| echo "${{ steps.gosec_compare.outputs.added_count }}" > tmp/gosec-summary/added.txt | |
| echo "${{ steps.gosec_compare.outputs.fixed_count }}" > tmp/gosec-summary/fixed.txt | |
| echo "${{ steps.gosec_compare.outputs.baseline_method }}" > tmp/gosec-summary/method.txt | |
| printf '%s' "$GOSEC_ADDED_DETAILS" > tmp/gosec-summary/added-details.txt | |
| printf '%s' "$GOSEC_FIXED_DETAILS" > tmp/gosec-summary/fixed-details.txt | |
| - name: Upload Gosec PR Summary Artifact | |
| if: github.event_name == 'pull_request' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-gosec-summary | |
| path: tmp/gosec-summary/ | |
| # - name: Create Pull Request (if vulnerabilities found) | |
| # if: ${{ github.event_name == 'push' && steps.scan.outputs.gosec_high_found == 'true' }} | |
| # continue-on-error: true | |
| # uses: peter-evans/create-pull-request@v5 | |
| # with: | |
| # commit-message: 'chore: vulnerabilities detected by Gosec (HIGH/CRITICAL)' | |
| # title: 'Gosec Vulnerability Report for branch ${{ github.ref_name }}' | |
| # body-path: tmp/pr-body.md | |
| # branch: auto/gosec-scan/${{ env.SAFE_REF_NAME }} | |
| # base: ${{ github.ref_name }} | |
| # delete-branch: true | |
| # add-paths: tmp/pr-body.md | |
| # Temporarily disabled: fail on vulnerabilities — fixing existing findings before re-enabling | |
| # - name: Fail Job If Vulnerabilities Found (Push) | |
| # if: ${{ github.event_name != 'pull_request' && steps.scan.outputs.gosec_high_found == 'true' }} | |
| # run: exit 1 | |
| # | |
| # - name: Fail Job If New Vulnerabilities Introduced (PR) | |
| # if: ${{ github.event_name == 'pull_request' && steps.gosec_compare.outputs.added_count > 0 }} | |
| # run: | | |
| # echo "New HIGH/CRITICAL vulnerabilities introduced: ${{ steps.gosec_compare.outputs.added_count }}" | |
| # exit 1 | |
| trivy_scan: | |
| name: Trivy Security Scan (Full) | |
| needs: setup | |
| runs-on: ubuntu-latest | |
| outputs: | |
| trivy_high_found: ${{ steps.scan.outputs.trivy_high_found }} | |
| trivy_current_count: ${{ steps.trivy_compare.outputs.current_count }} | |
| trivy_baseline_count: ${{ steps.trivy_compare.outputs.baseline_count }} | |
| trivy_added_count: ${{ steps.trivy_compare.outputs.added_count }} | |
| trivy_fixed_count: ${{ steps.trivy_compare.outputs.fixed_count }} | |
| trivy_baseline_method: ${{ steps.trivy_compare.outputs.baseline_method }} | |
| trivy_added_details: ${{ steps.trivy_compare.outputs.added_details }} | |
| trivy_fixed_details: ${{ steps.trivy_compare.outputs.fixed_details }} | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| - name: Install Trivy | |
| run: | | |
| TRIVY_VERSION=$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest | jq -r .tag_name | sed 's/v//') | |
| wget https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.deb | |
| sudo dpkg -i trivy_${TRIVY_VERSION}_Linux-64bit.deb | |
| sudo apt install -f -y | |
| sudo apt install -y jq | |
| - name: Sanitize branch name | |
| run: echo "SAFE_REF_NAME=${GITHUB_REF_NAME//\//-}" >> $GITHUB_ENV | |
| - name: Run Trivy Filesystem Scan | |
| id: scan | |
| run: | | |
| echo "Running Trivy scan (HIGH/CRITICAL)..." | |
| mkdir -p tmp | |
| trivy fs --format json --severity HIGH,CRITICAL --output tmp/trivy-report.json . | |
| [[ -f tmp/trivy-report.json ]] || echo '{"Results":[]}' > tmp/trivy-report.json | |
| count=$(jq -e ' | |
| (.Results // []) | |
| | map(.Vulnerabilities? // []) | |
| | add | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | length | |
| ' tmp/trivy-report.json || echo 0) | |
| if [[ "$count" -gt 0 ]]; then | |
| echo "trivy_high_found=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "trivy_high_found=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Upload Trivy Report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: trivy-json-${{ env.SAFE_REF_NAME }} | |
| path: tmp/trivy-report.json | |
| - name: Generate PR Body (if vulnerabilities found) | |
| if: ${{ steps.scan.outputs.trivy_high_found == 'true' }} | |
| run: | | |
| echo "# 🛡️ Trivy Scan Report for branch \`${GITHUB_REF_NAME}\`" > tmp/pr-body.md | |
| jq -r ' | |
| (.Results // []) | |
| | .[] | |
| | .Target as $file | |
| | (.Vulnerabilities? // []) | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | .[] | |
| | "* File: \($file)\n • Vulnerability ID: \(.VulnerabilityID)\n • Pkg: \(.PkgName) \(.InstalledVersion)\n • Severity: \(.Severity)\n • Title: \(.Title)\n" | |
| ' tmp/trivy-report.json >> tmp/pr-body.md | |
| - name: Debug - List Available Trivy Artifacts | |
| if: github.event_name == 'pull_request' | |
| continue-on-error: true | |
| run: | | |
| echo "Looking for Trivy artifacts from base SHA: ${{ github.event.pull_request.base.sha }}" | |
| echo "Expected Trivy artifact: trivy-filesystem-report-${{ github.event.pull_request.base.sha }}" | |
| echo "Base branch: ${{ github.event.pull_request.base.ref }}" | |
| - name: Download Baseline Trivy Report (PR only) | |
| if: github.event_name == 'pull_request' | |
| continue-on-error: true | |
| uses: dawidd6/action-download-artifact@v3 | |
| with: | |
| name: trivy-filesystem-report-${{ github.event.pull_request.base.sha }} | |
| path: ./baseline-reports | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| workflow: security.yml | |
| branch: ${{ github.event.pull_request.base.ref }} | |
| workflow_conclusion: "" | |
| check_artifacts: true | |
| if_no_artifact_found: warn | |
| repo: ${{ github.repository }} | |
| search_artifacts: true | |
| - name: Compare Trivy Vulnerabilities with Baseline (PR only) | |
| if: github.event_name == 'pull_request' | |
| id: trivy_compare | |
| run: | | |
| echo "Comparing Trivy vulnerabilities with baseline..." | |
| # Initialize counts | |
| current_count=0 | |
| baseline_count=0 | |
| added_count=0 | |
| fixed_count=0 | |
| baseline_method="none" | |
| # Count current vulnerabilities | |
| if [[ -f "tmp/trivy-report.json" ]]; then | |
| current_count=$(jq -e ' | |
| (.Results // []) | |
| | map(.Vulnerabilities? // []) | |
| | add | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | length | |
| ' tmp/trivy-report.json || echo 0) | |
| fi | |
| # Try to use artifact baseline first | |
| if [[ -f "baseline-reports/trivy-report.json" ]]; then | |
| echo "✅ Using artifact baseline from main branch" | |
| baseline_method="artifact" | |
| baseline_count=$(jq -e ' | |
| (.Results // []) | |
| | map(.Vulnerabilities? // []) | |
| | add | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | length | |
| ' baseline-reports/trivy-report.json || echo 0) | |
| # Create unique vulnerability identifiers for comparison | |
| jq -r ' | |
| (.Results // []) | |
| | .[] | |
| | .Target as $file | |
| | (.Vulnerabilities? // []) | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | .[] | |
| | "\($file):\(.VulnerabilityID):\(.PkgName)" | |
| ' tmp/trivy-report.json | sort > tmp/current-trivy-vulns.txt | |
| jq -r ' | |
| (.Results // []) | |
| | .[] | |
| | .Target as $file | |
| | (.Vulnerabilities? // []) | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | .[] | |
| | "\($file):\(.VulnerabilityID):\(.PkgName)" | |
| ' baseline-reports/trivy-report.json | sort > tmp/baseline-trivy-vulns.txt | |
| # Calculate added and fixed vulnerabilities | |
| added_count=$(comm -23 tmp/current-trivy-vulns.txt tmp/baseline-trivy-vulns.txt | wc -l) | |
| fixed_count=$(comm -13 tmp/current-trivy-vulns.txt tmp/baseline-trivy-vulns.txt | wc -l) | |
| else | |
| echo "⚠️ No baseline artifact found, scanning base branch live..." | |
| baseline_method="live_scan" | |
| # Save current state | |
| cp tmp/trivy-report.json tmp/trivy-report-pr.json | |
| # Checkout and scan base branch | |
| git fetch origin ${{ github.event.pull_request.base.ref }} | |
| git checkout ${{ github.event.pull_request.base.sha }} | |
| # Run baseline scan | |
| trivy fs --format json --severity HIGH,CRITICAL --output tmp/trivy-report-baseline.json . | |
| [[ -f tmp/trivy-report-baseline.json ]] || echo '{"Results":[]}' > tmp/trivy-report-baseline.json | |
| baseline_count=$(jq -e ' | |
| (.Results // []) | |
| | map(.Vulnerabilities? // []) | |
| | add | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | length | |
| ' tmp/trivy-report-baseline.json || echo 0) | |
| # Create comparison files | |
| jq -r ' | |
| (.Results // []) | |
| | .[] | |
| | .Target as $file | |
| | (.Vulnerabilities? // []) | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | .[] | |
| | "\($file):\(.VulnerabilityID):\(.PkgName)" | |
| ' tmp/trivy-report-pr.json | sort > tmp/current-trivy-vulns.txt | |
| jq -r ' | |
| (.Results // []) | |
| | .[] | |
| | .Target as $file | |
| | (.Vulnerabilities? // []) | |
| | map(select(.Severity=="HIGH" or .Severity=="CRITICAL")) | |
| | .[] | |
| | "\($file):\(.VulnerabilityID):\(.PkgName)" | |
| ' tmp/trivy-report-baseline.json | sort > tmp/baseline-trivy-vulns.txt | |
| # Calculate differences | |
| added_count=$(comm -23 tmp/current-trivy-vulns.txt tmp/baseline-trivy-vulns.txt | wc -l) | |
| fixed_count=$(comm -13 tmp/current-trivy-vulns.txt tmp/baseline-trivy-vulns.txt | wc -l) | |
| # Restore PR state | |
| git checkout ${{ github.sha }} | |
| cp tmp/trivy-report-pr.json tmp/trivy-report.json | |
| fi | |
| echo "Baseline method: $baseline_method" | |
| echo "Current Trivy HIGH/CRITICAL vulnerabilities: $current_count" | |
| echo "Baseline Trivy HIGH/CRITICAL vulnerabilities: $baseline_count" | |
| echo "Added Trivy vulnerabilities: $added_count" | |
| echo "Fixed Trivy vulnerabilities: $fixed_count" | |
| # Generate detailed vulnerability lists | |
| added_details="" | |
| fixed_details="" | |
| if [[ -f "tmp/current-trivy-vulns.txt" && -f "tmp/baseline-trivy-vulns.txt" ]]; then | |
| # Get added vulnerabilities with details | |
| if [[ $added_count -gt 0 ]]; then | |
| added_vulns=$(comm -23 tmp/current-trivy-vulns.txt tmp/baseline-trivy-vulns.txt) | |
| added_details="" | |
| while IFS= read -r vuln_id; do | |
| if [[ -n "$vuln_id" ]]; then | |
| target=$(echo "$vuln_id" | cut -d':' -f1) | |
| vuln_cve=$(echo "$vuln_id" | cut -d':' -f2) | |
| pkg_name=$(echo "$vuln_id" | cut -d':' -f3) | |
| details=$(jq -r --arg target "$target" --arg vuln "$vuln_cve" --arg pkg "$pkg_name" ' | |
| (.Results // []) | .[] | select(.Target == $target) | | |
| (.Vulnerabilities? // []) | .[] | | |
| select(.VulnerabilityID == $vuln and .PkgName == $pkg and (.Severity == "HIGH" or .Severity == "CRITICAL")) | | |
| "**Target:** `\(.Target // $target)`\\n**Package:** \(.PkgName) \(.InstalledVersion)\\n**Vulnerability:** \(.VulnerabilityID)\\n**Severity:** \(.Severity)\\n**Title:** \(.Title)\\n" | |
| ' tmp/trivy-report.json) | |
| if [[ -n "$details" ]]; then | |
| added_details="${added_details}${details}\\n" | |
| fi | |
| fi | |
| done <<< "$added_vulns" | |
| fi | |
| # Get fixed vulnerabilities with details | |
| if [[ $fixed_count -gt 0 ]]; then | |
| fixed_vulns=$(comm -13 tmp/current-trivy-vulns.txt tmp/baseline-trivy-vulns.txt) | |
| fixed_details="" | |
| while IFS= read -r vuln_id; do | |
| if [[ -n "$vuln_id" ]]; then | |
| target=$(echo "$vuln_id" | cut -d':' -f1) | |
| vuln_cve=$(echo "$vuln_id" | cut -d':' -f2) | |
| pkg_name=$(echo "$vuln_id" | cut -d':' -f3) | |
| baseline_report="baseline-reports/trivy-report.json" | |
| if [[ "$baseline_method" == "live_scan" ]]; then | |
| baseline_report="tmp/trivy-report-baseline.json" | |
| fi | |
| details=$(jq -r --arg target "$target" --arg vuln "$vuln_cve" --arg pkg "$pkg_name" ' | |
| (.Results // []) | .[] | select(.Target == $target) | | |
| (.Vulnerabilities? // []) | .[] | | |
| select(.VulnerabilityID == $vuln and .PkgName == $pkg and (.Severity == "HIGH" or .Severity == "CRITICAL")) | | |
| "**Target:** `\(.Target // $target)`\\n**Package:** \(.PkgName) \(.InstalledVersion)\\n**Vulnerability:** \(.VulnerabilityID)\\n**Severity:** \(.Severity)\\n**Title:** \(.Title)\\n" | |
| ' "$baseline_report") | |
| if [[ -n "$details" ]]; then | |
| fixed_details="${fixed_details}${details}\\n" | |
| fi | |
| fi | |
| done <<< "$fixed_vulns" | |
| fi | |
| fi | |
| # Set outputs | |
| echo "current_count=$current_count" >> "$GITHUB_OUTPUT" | |
| echo "baseline_count=$baseline_count" >> "$GITHUB_OUTPUT" | |
| echo "added_count=$added_count" >> "$GITHUB_OUTPUT" | |
| echo "fixed_count=$fixed_count" >> "$GITHUB_OUTPUT" | |
| echo "baseline_method=$baseline_method" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "added_details<<EOF" | |
| echo -e "$added_details" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| { | |
| echo "fixed_details<<EOF" | |
| echo -e "$fixed_details" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Display Trivy Vulnerability Report | |
| if: ${{ steps.scan.outputs.trivy_high_found == 'true' }} | |
| run: | | |
| echo "==========================================" | |
| echo "Trivy HIGH/CRITICAL Vulnerability Report" | |
| echo "==========================================" | |
| printf "%-50s %-20s %-25s %-10s %s\n" "TARGET" "VULNERABILITY" "PACKAGE" "SEVERITY" "TITLE" | |
| printf "%-50s %-20s %-25s %-10s %s\n" "------" "-------------" "-------" "--------" "-----" | |
| jq -r ' | |
| (.Results // []) | |
| | .[] | |
| | .Target as $target | |
| | (.Vulnerabilities? // []) | |
| | map(select(.Severity == "HIGH" or .Severity == "CRITICAL")) | |
| | .[] | |
| | [$target, .VulnerabilityID, (.PkgName + " " + .InstalledVersion), .Severity, .Title] | |
| | @tsv | |
| ' tmp/trivy-report.json | while IFS=$'\t' read -r target vuln pkg sev title; do | |
| printf "%-50s %-20s %-25s %-10s %s\n" "$target" "$vuln" "$pkg" "$sev" "$title" | |
| done | |
| echo "==========================================" | |
| - name: Store Trivy Filesystem Report as Artifact (Main/Release) | |
| if: ${{ (github.ref == 'refs/heads/main' && github.event_name == 'push') || github.event_name == 'release' }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: trivy-filesystem-report-${{ github.sha }} | |
| path: tmp/trivy-report.json | |
| retention-days: 90 | |
| - name: Save Trivy PR Summary for Comment Workflow | |
| if: github.event_name == 'pull_request' | |
| env: | |
| TRIVY_ADDED_DETAILS: ${{ steps.trivy_compare.outputs.added_details }} | |
| TRIVY_FIXED_DETAILS: ${{ steps.trivy_compare.outputs.fixed_details }} | |
| run: | | |
| mkdir -p tmp/trivy-summary | |
| echo "${{ github.event.pull_request.number }}" > tmp/trivy-summary/pr-number.txt | |
| echo "${{ github.event.pull_request.base.sha }}" > tmp/trivy-summary/base-sha.txt | |
| echo "${{ steps.trivy_compare.outputs.current_count }}" > tmp/trivy-summary/current.txt | |
| echo "${{ steps.trivy_compare.outputs.baseline_count }}" > tmp/trivy-summary/baseline.txt | |
| echo "${{ steps.trivy_compare.outputs.added_count }}" > tmp/trivy-summary/added.txt | |
| echo "${{ steps.trivy_compare.outputs.fixed_count }}" > tmp/trivy-summary/fixed.txt | |
| echo "${{ steps.trivy_compare.outputs.baseline_method }}" > tmp/trivy-summary/method.txt | |
| printf '%s' "$TRIVY_ADDED_DETAILS" > tmp/trivy-summary/added-details.txt | |
| printf '%s' "$TRIVY_FIXED_DETAILS" > tmp/trivy-summary/fixed-details.txt | |
| - name: Upload Trivy PR Summary Artifact | |
| if: github.event_name == 'pull_request' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-trivy-summary | |
| path: tmp/trivy-summary/ | |
| # - name: Create Pull Request (if vulnerabilities found) | |
| # if: ${{ github.event_name == 'push' && steps.scan.outputs.trivy_high_found == 'true' }} | |
| # continue-on-error: true | |
| # uses: peter-evans/create-pull-request@v5 | |
| # with: | |
| # commit-message: 'chore: vulnerabilities detected by Trivy (HIGH/CRITICAL)' | |
| # title: 'Trivy Vulnerability Report for branch ${{ github.ref_name }}' | |
| # body-path: tmp/pr-body.md | |
| # branch: auto/trivy-scan/${{ env.SAFE_REF_NAME }} | |
| # base: ${{ github.ref_name }} | |
| # delete-branch: true | |
| # add-paths: tmp/pr-body.md | |
| # Temporarily disabled: fail on vulnerabilities — fixing existing findings before re-enabling | |
| # - name: Fail Job If Vulnerabilities Found (Push) | |
| # if: ${{ github.event_name != 'pull_request' && steps.scan.outputs.trivy_high_found == 'true' }} | |
| # run: exit 1 | |
| # | |
| # - name: Fail Job If New Vulnerabilities Introduced (PR) | |
| # if: ${{ github.event_name == 'pull_request' && steps.trivy_compare.outputs.added_count > 0 }} | |
| # run: | | |
| # echo "New HIGH/CRITICAL vulnerabilities introduced: ${{ steps.trivy_compare.outputs.added_count }}" | |
| # exit 1 | |
| # vulnerability_summary job moved to security-comment.yml workflow | |
| # which uses workflow_run trigger to post PR comments with full write permissions, | |
| # including for fork PRs where pull_request trigger tokens are read-only. |