Dependency Scan #12
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: Dependency Scan | |
| on: | |
| schedule: | |
| # Monthly: 1st of each month at 09:00 UTC | |
| - cron: '0 9 1 * *' | |
| workflow_dispatch: | |
| inputs: | |
| fail-on: | |
| description: 'Fail on labels (comma-separated, empty = never fail)' | |
| required: false | |
| default: 'eol-confirmed' | |
| extra-args: | |
| description: 'Extra arguments passed to uzomuzo scan' | |
| required: false | |
| default: '' | |
| create-issue: | |
| description: 'Create a GitHub Issue with scan report' | |
| required: false | |
| default: true | |
| type: boolean | |
| permissions: | |
| contents: read | |
| jobs: | |
| scan: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| exit_code: ${{ steps.scan.outputs.exit_code }} | |
| scan_output: ${{ steps.scan.outputs.scan_output }} | |
| fail_on: ${{ steps.scan.outputs.fail_on }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| - name: Build uzomuzo | |
| run: go build -o uzomuzo ./cmd/uzomuzo | |
| - name: Generate SBOM | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| with: | |
| scan-type: 'fs' | |
| format: 'cyclonedx' | |
| output: 'sbom.json' | |
| - name: Run scan | |
| id: scan | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| EXTRA_ARGS: ${{ github.event.inputs.extra-args || '' }} | |
| FAIL_ON_INPUT: ${{ github.event.inputs.fail-on }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| # For schedule runs, default to eol-confirmed. | |
| # For workflow_dispatch, respect the input (empty = never fail). | |
| if [ "$EVENT_NAME" = "schedule" ]; then | |
| FAIL_ON="${FAIL_ON_INPUT:-eol-confirmed}" | |
| else | |
| FAIL_ON="$FAIL_ON_INPUT" | |
| fi | |
| ARGS=() | |
| if [ -n "$FAIL_ON" ]; then | |
| ARGS+=("--fail-on" "$FAIL_ON") | |
| fi | |
| if [ -n "$EXTRA_ARGS" ]; then | |
| mapfile -t EXTRA_ARGS_ARRAY < <(python3 -c 'import os, shlex; [print(arg) for arg in shlex.split(os.environ["EXTRA_ARGS"])]') | |
| ARGS+=("${EXTRA_ARGS_ARRAY[@]}") | |
| fi | |
| # Single scan run; detailed format includes summary table + detailed report | |
| set +e | |
| ./uzomuzo scan --sbom sbom.json --format detailed "${ARGS[@]}" 2>&1 | tee scan-report.txt | |
| EXIT_CODE=${PIPESTATUS[0]} | |
| set -e | |
| echo "exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT" | |
| echo "fail_on=$FAIL_ON" >> "$GITHUB_OUTPUT" | |
| # Extract summary table section (between markers) for issue body | |
| { | |
| echo 'scan_output<<SCAN_EOF' | |
| sed -n '/^--- Summary Table ---$/,/^--- Detailed Report ---$/{ /^--- Detailed Report ---$/d; p; }' scan-report.txt | |
| echo 'SCAN_EOF' | |
| } >> "$GITHUB_OUTPUT" | |
| exit $EXIT_CODE | |
| - name: Upload scan report | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: dependency-scan-report | |
| path: | | |
| sbom.json | |
| scan-report.txt | |
| retention-days: 90 | |
| report: | |
| needs: scan | |
| if: always() && needs.scan.outputs.exit_code != '' && github.event.inputs.create-issue != 'false' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| steps: | |
| - name: Create monthly scan report issue | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| SCAN_OUTPUT: ${{ needs.scan.outputs.scan_output }} | |
| EXIT_CODE: ${{ needs.scan.outputs.exit_code }} | |
| FAIL_ON_POLICY: ${{ needs.scan.outputs.fail_on }} | |
| run: | | |
| TITLE="Dependency scan report ($(date +%Y-%m-%d))" | |
| # Deduplicate: skip if an open issue with the same title exists | |
| EXISTING=$(gh issue list --repo "${{ github.repository }}" --state open --search "in:title $TITLE" --json title | jq --arg t "$TITLE" '[.[] | select(.title == $t)] | length') | |
| if [ "$EXISTING" -gt 0 ]; then | |
| echo "Open issue already exists, skipping creation" | |
| exit 0 | |
| fi | |
| if [ "$EXIT_CODE" != "0" ]; then | |
| STATUS="Policy violations detected" | |
| else | |
| STATUS="No policy violations" | |
| fi | |
| BODY=$(cat <<EOF | |
| ## Dependency Scan Report | |
| **Status**: $STATUS | |
| **Workflow run**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| **Fail-on policy**: \`${FAIL_ON_POLICY:-(none)}\` | |
| <details> | |
| <summary>Scan output</summary> | |
| \`\`\` | |
| $SCAN_OUTPUT | |
| \`\`\` | |
| </details> | |
| For detailed per-dependency analysis, download the \`dependency-scan-report\` artifact from the workflow run. | |
| EOF | |
| ) | |
| gh issue create --repo "${{ github.repository }}" --title "$TITLE" --body "$BODY" --label "dependencies" |