diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 4ce7dcc..860a2b7 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -10,12 +10,26 @@ on: schedule: - cron: '0 0 * * 0' # Weekly on Sunday workflow_dispatch: +# Estate guardrail: cancel superseded runs so re-pushes don't pile up +# queued runs across the estate. Safe here because this workflow only +# performs read-only checks/lint/test/scan with no publish or mutation. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true permissions: contents: read # security-events: read lets the built-in GITHUB_TOKEN query this - # repo\'s own Dependabot alerts via the Hypatia DependabotAlerts rule. + # repo's own Dependabot alerts via the Hypatia DependabotAlerts rule + # (DA001-DA004). Without this, `scan_from_path` gets HTTP 403 and + # the rule silently returns no findings. + # See 007-lang/audits/audit-dependabot-automation-gap-2026-04-17.md. security-events: read + # pull-requests: write lets the advisory "Comment on PR with findings" + # step post its summary. Without it the built-in GITHUB_TOKEN gets + # "Resource not accessible by integration" and (absent continue-on-error) + # hard-fails the scan — exactly what the gate-decoupling design forbids. + pull-requests: write jobs: scan: @@ -24,12 +38,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # Full history for better pattern analysis - name: Setup Elixir for Hypatia scanner - uses: erlef/setup-beam@ee09b1e59bb240681c382eb1f0abc6a04af72764 # v1.18.2 + uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.18.2 with: elixir-version: '1.19.4' otp-version: '28.3' @@ -41,23 +55,27 @@ jobs: fi - name: Build Hypatia scanner (if needed) - working-directory: /home/runner/hypatia run: | - if [ ! -f hypatia-v2 ]; then - echo "Building hypatia-v2 scanner..." - cd scanner + cd "$HOME/hypatia" + if [ ! -f hypatia ]; then + echo "Building hypatia scanner..." mix deps.get mix escript.build - mv hypatia ../hypatia-v2 fi - name: Run Hypatia scan id: scan + env: + # Pass the built-in Actions token through to Hypatia so the + # DependabotAlerts rule can query this repo's own alerts. + # For cross-repo scanning (fleet-coordinator scan-supervised), + # a PAT with `security_events` scope is required instead. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | echo "Scanning repository: ${{ github.repository }}" - # Run scanner - HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . > hypatia-findings.json + # Run scanner (exits non-zero when findings exist — suppress to continue) + HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia-findings.json || true # Count findings FINDING_COUNT=$(jq '. | length' hypatia-findings.json 2>/dev/null || echo 0) @@ -79,7 +97,7 @@ jobs: echo "- Medium: $MEDIUM" >> $GITHUB_STEP_SUMMARY - name: Upload findings artifact - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: hypatia-findings path: hypatia-findings.json @@ -198,6 +216,11 @@ jobs: - name: Comment on PR with findings if: github.event_name == 'pull_request' && steps.scan.outputs.findings_count > 0 + # Advisory only — posting findings as a PR comment must never gate + # the scan (hypatia#213 gate decoupling). Belt-and-braces alongside + # the pull-requests: write permission above: a token/API hiccup or + # a fork PR (read-only token) skips the comment, not the check. + continue-on-error: true uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v7 with: script: | @@ -228,4 +251,4 @@ jobs: repo: context.repo.repo, issue_number: context.issue.number, body: comment - }); + }); \ No newline at end of file