diff --git a/.github/workflows/scan-darkwing-portable.yml b/.github/workflows/scan-darkwing-portable.yml new file mode 100644 index 00000000..2c82d729 --- /dev/null +++ b/.github/workflows/scan-darkwing-portable.yml @@ -0,0 +1,32 @@ +# Darkwing vulnerability scan, manual dispatch. +# +# Copied verbatim from the ge-aero/darkwing README so it stays byte-identical +# to the sanctioned integration. Do not edit this file to add triggers or +# gating - see scan-darkwing-pr.yml for that, which is ours to change. +# +# The file must sit on the default branch before a scan can run on ANY branch. +name: Scan (darkwing, portable) + +on: + workflow_dispatch: + inputs: + target_ref: + description: Ref, branch, or sha of this repo to scan + required: false + type: string + default: main + +permissions: + contents: read # checkout this repo via the ambient GITHUB_TOKEN + security-events: write # SARIF upload to the Security tab + actions: read # required by upload-sarif on private/internal repos + +jobs: + scan: + runs-on: darkwing-scan + steps: + # @v1 is a moving tag pointing to the latest release that is compatible + # with this workflow file. + - uses: ge-aero/darkwing@v1 + with: + target_ref: ${{ inputs.target_ref || 'main' }} diff --git a/.github/workflows/scan-darkwing-pr.yml b/.github/workflows/scan-darkwing-pr.yml new file mode 100644 index 00000000..12ed8d89 --- /dev/null +++ b/.github/workflows/scan-darkwing-pr.yml @@ -0,0 +1,62 @@ +# Darkwing scan on pull requests, so the "check against darkwing" is a check +# and not a thing somebody remembers to click. +# +# WHY THIS IS SEPARATE from scan-darkwing-portable.yml: that file is copied +# byte-for-byte from the ge-aero/darkwing README and is left alone so it keeps +# matching the sanctioned integration. Everything below is ours. +# +# THE TRAP THIS AVOIDS. The README workflow resolves its scan target as +# `inputs.target_ref || 'main'`. On a pull_request event there are no inputs, +# so that expression falls through to 'main' and the job scans the default +# branch instead of the code under review - a green Darkwing check that never +# looked at the change. Resolve the head SHA explicitly. +# +# Darkwing exits 0 whatever it finds (its own FAQ: "Does a finding fail my +# build? No."), so a passing job means the scan RAN, not that the code is +# clean. Findings land in Security -> Code scanning. Turning a finding into a +# hard failure means reading findings.sarif ourselves; the commented step at +# the bottom is the shape of that, deliberately left off until we have seen the +# false-positive rate on this repo. +name: Scan (darkwing, PR) + +on: + pull_request: + +permissions: + contents: read + security-events: write + actions: read + +# One scan per PR. A new push supersedes the old one rather than queueing a +# second Bedrock spend against a diff nobody is reviewing any more. +concurrency: + group: darkwing-pr-${{ github.ref }} + cancel-in-progress: true + +jobs: + scan: + runs-on: darkwing-scan + steps: + - uses: ge-aero/darkwing@v1 + with: + # The PR's head, NOT the default branch. See the note above. + target_ref: ${{ github.event.pull_request.head.sha }} + + # Gating, once the signal is trusted. Darkwing writes findings.sarif into + # its work dir and attaches it as the `findings` artifact; this reads the + # severities out and fails on error-level results only. + # + # - name: Fail on error-level findings + # run: | + # python - <<'PY' + # import json, pathlib, sys + # sarif = pathlib.Path('darkwing/findings.sarif') + # if not sarif.is_file(): + # sys.exit('no findings.sarif: the scan did not produce results') + # doc = json.loads(sarif.read_text()) + # bad = [r for run in doc.get('runs', []) + # for r in run.get('results', []) + # if r.get('level') == 'error'] + # if bad: + # sys.exit(f'{len(bad)} error-level finding(s); see the Security tab') + # PY