diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index 1278aa4..eaf0f65 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -63,20 +63,28 @@ jobs: extra-substituters: ${{ inputs.extra-substituters }} extra-trusted-public-keys: ${{ inputs.extra-trusted-public-keys }} + # Build the dev shell in its own step so that the Nix build log — hundreds + # of kilobytes of downloads and `building ...` lines on a cold cache — + # stays out of the report captured below. + - name: Warm the dev shell + run: nix develop --command true + - name: Check advisories id: audit run: | set +e - OUTPUT=$(nix develop --command cargo deny check --config .config/deny.toml advisories 2>&1) + nix develop --command cargo deny check --config .config/deny.toml advisories \ + > "$REPORT" 2>&1 EXIT_CODE=$? - echo "$OUTPUT" - { - echo "output<> "$GITHUB_OUTPUT" + cat "$REPORT" echo "exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT" exit 0 + env: + # The report goes to a file, not a step output: it is passed to the + # reporting step below, where an inlined copy would land in the + # environment block and fail the step with E2BIG once it exceeds the + # 128 KiB per-string execve limit. + REPORT: ${{ runner.temp }}/cargo-deny-advisories.txt - name: Find existing issue id: find_issue @@ -89,38 +97,56 @@ jobs: - name: Create or update issue on failure if: steps.audit.outputs.exit_code != '0' run: | + set -eo pipefail TITLE="Security Advisory Alert" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - BODY=$(cat <<'ISSUE_EOF' - ## Security Advisory Found - - `cargo deny check advisories` found active advisories in dependencies. - -
- Full output + BODY_FILE="${RUNNER_TEMP}/security-advisory-issue.md" - ``` - __AUDIT_OUTPUT__ - ``` - -
- - **Action required:** Review the advisories above and update affected dependencies or add ignore entries to `.config/deny.toml` if appropriate. + # A GitHub issue body is capped at 65536 characters, so a very large + # report cannot be posted whole. Keep the tail, which is where + # cargo-deny prints the advisories and its verdict, and link the run + # log for the rest. `tail -n +2` drops the partial first line left by + # the byte-wise cut, which could otherwise split a UTF-8 sequence. + MAX_REPORT_BYTES=50000 + TRUNCATED="" + if [ "$(wc -c < "$REPORT")" -gt "$MAX_REPORT_BYTES" ]; then + TRUNCATED=yes + tail -c "$MAX_REPORT_BYTES" "$REPORT" | tail -n +2 > "${REPORT}.tail" + mv "${REPORT}.tail" "$REPORT" + fi - _Last checked: __TIMESTAMP___ - ISSUE_EOF - ) - BODY="${BODY//__TIMESTAMP__/$TIMESTAMP}" - BODY="${BODY//__AUDIT_OUTPUT__/$AUDIT_OUTPUT}" + { + echo "## Security Advisory Found" + echo + echo "\`cargo deny check advisories\` found active advisories in dependencies." + echo + if [ -n "$TRUNCATED" ]; then + echo "> Output truncated to the last ${MAX_REPORT_BYTES} bytes. The full report is in the [workflow run log](${RUN_URL})." + echo + fi + echo "
" + echo "Full output" + echo + echo '```' + cat "$REPORT" + echo + echo '```' + echo + echo "
" + echo + echo "**Action required:** Review the advisories above and update affected dependencies or add ignore entries to \`.config/deny.toml\` if appropriate." + echo + echo "_Last checked: $(date -u +%Y-%m-%dT%H:%M:%SZ)_" + } > "$BODY_FILE" if [ -n "$ISSUE_NUMBER" ]; then - gh issue edit "$ISSUE_NUMBER" --body "$BODY" + gh issue edit "$ISSUE_NUMBER" --body-file "$BODY_FILE" else - gh issue create --title "$TITLE" --body "$BODY" --label security + gh issue create --title "$TITLE" --body-file "$BODY_FILE" --label security fi env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - AUDIT_OUTPUT: ${{ steps.audit.outputs.output }} + REPORT: ${{ runner.temp }}/cargo-deny-advisories.txt + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} ISSUE_NUMBER: ${{ steps.find_issue.outputs.number }} - name: Close issue on success