Upload new fuzz corpus entries as a short-lived CI artifact
What changed, and why it matters
This commit changes a CI workflow for the rust-lightning project. It stops trying to push new fuzz test inputs directly to a corpus repository from automated test runs, and instead uploads them as a temporary artifact that a separate scheduled job later collects. This is a workflow reliability and credential-handling improvement, not a security fix or vulnerability.
No security action required. This is a CI workflow change. Reviewers may want to verify artifact retention and permissions are as intended, and that the nightly sweep job correctly processes and deletes artifacts.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change modifies .forgejo/workflows/build.yml. It replaces a step that opened a pull request on the ldk-fuzzing-corpus repository using a push token with a two-step process: (1) stage newly generated fuzz corpus files and SIG* crash files into a local directory, and (2) upload that directory as a short-lived hfuzz-corpus CI artifact using the Forgejo-native upload-artifact action with a 2-day retention. The corpus repository’s nightly job is responsible for sweeping these artifacts into pull requests. The change also clones the corpus from the same Forgejo instance rather than GitHub, and fixes the crash-staging path prefix to rust-lightning/<target>.
Changed components
.forgejo/workflows/build.ymlInspect captured patch +52 / −45
diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml
index 1278a38..2a0d917 100644
--- a/.forgejo/workflows/build.yml
+++ b/.forgejo/workflows/build.yml
@@ -273,7 +273,10 @@ jobs:
run: |
rustup default 1.75
- name: Clone fuzzing corpus
- run: git clone --depth=1 https://github.com/lightningdevkit/ldk-fuzzing-corpus.git fuzz/ldk-fuzzing-corpus
+ # Clone from this Forgejo instance (rather than the GitHub copy) so
+ # that new entries are detected against the repository the corpus
+ # sweep will open its pull requests on.
+ run: git clone --depth=1 ${{ github.server_url }}/lightningdevkit/ldk-fuzzing-corpus.git fuzz/ldk-fuzzing-corpus
- name: Symlink corpus into hfuzz_workspace
run: |
set -eu
@@ -288,55 +291,59 @@ jobs:
run: cd fuzz && ./ci-fuzz.sh && cd ..
env:
FUZZ_MINIMIZE: ${{ contains(github.event.pull_request.labels.*.name, 'fuzz-minimize') }}
- - name: Open PR with new corpus entries
- # Forgejo supports neither the `workflow_run` trigger nor reading
- # artifacts from another workflow run, so the corpus push that used to
- # live in its own workflow is folded in here. New fuzzer inputs are
- # written straight into the corpus checkout (the input dirs are
- # symlinked into it above), so they show up as untracked files.
- #
- # The push still targets the GitHub corpus repo. On Forgejo, secrets
- # are empty for `pull_request` events from forks, so CORPUS_PUSH_TOKEN
- # is unset there and this step safely skips the push.
+ - name: Stage new corpus entries for upload
+ # New fuzzer inputs are written straight into the corpus checkout (the
+ # input dirs are symlinked into it above), so they show up as
+ # untracked files there.
#
- # A push hiccup must not fail the fuzz job (and cascade to the jobs that
- # depend on it), so this step is best-effort.
+ # This run can't contribute them to the corpus repo itself: it mostly
+ # runs for pull requests from forks, and Forgejo withholds all
+ # credentials (secrets and identity tokens alike) from fork-PR runs.
+ # Instead the new entries are uploaded as a short-lived artifact
+ # below, which the corpus repo's nightly job sweeps into a pull
+ # request.
if: success() || failure()
- continue-on-error: true
- env:
- GH_TOKEN: ${{ secrets.CORPUS_PUSH_TOKEN }}
- SOURCE_SHA: ${{ github.sha }}
- RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}
- RUN_ID: ${{ github.run_id }}
run: |
set -eu
+ WORKSPACE="$(pwd)"
+ rm -rf "$WORKSPACE/new-corpus"
+ mkdir -p "$WORKSPACE/new-corpus"
+
cd fuzz/ldk-fuzzing-corpus
- if [ -z "$(git status --porcelain)" ]; then
- echo "No new corpus entries to contribute."
- exit 0
- fi
- if [ -z "${GH_TOKEN:-}" ]; then
- echo "Found new corpus entries but CORPUS_PUSH_TOKEN is unset; skipping PR."
- git status --short
- exit 0
- fi
- BRANCH="ci/new-corpus-${RUN_ID}"
- git config user.email "ldk-ci@users.noreply.github.com"
- git config user.name "LDK CI"
- git checkout -b "$BRANCH"
- git add rust-lightning
- git commit \
- -m "Add corpus entries from rust-lightning CI" \
- -m "Source commit: ${SOURCE_SHA}" \
- -m "Run: ${RUN_URL}"
- REMOTE=$(git config --get remote.origin.url)
- PUSH_URL="https://x-access-token:${GH_TOKEN}@${REMOTE#https://}"
- git push "$PUSH_URL" "HEAD:$BRANCH"
- gh pr create \
- --title "New corpus entries from rust-lightning CI run ${RUN_ID}" \
- --body "Discovered while running fuzz CI against \`${SOURCE_SHA}\`. Source: ${RUN_URL}" \
- --head "$BRANCH" \
- --base master
+ while IFS= read -r F; do
+ mkdir -p "$WORKSPACE/new-corpus/$(dirname "$F")"
+ cp -a "$F" "$WORKSPACE/new-corpus/$F"
+ done < <(git ls-files --others --exclude-standard rust-lightning/)
+ cd "$WORKSPACE"
+
+ for D in fuzz/hfuzz_workspace/*_target/; do
+ [ -d "$D" ] || continue
+ BASE=$(basename "$D")
+ NAME="${BASE%_target}"
+ for F in "$D"/SIG*; do
+ [ -f "$F" ] || continue
+ FILE="$(basename "$F")"
+ [ -f "$WORKSPACE/new-corpus/rust-lightning/$NAME/$FILE" ] && continue
+ mkdir -p "$WORKSPACE/new-corpus/rust-lightning/$NAME"
+ cp "$F" "$WORKSPACE/new-corpus/rust-lightning/$NAME/$FILE"
+ done
+ done
+
+ NEW=$(find new-corpus -type f 2>/dev/null | wc -l)
+ echo "Staged $NEW new corpus entries (including any SIG* crashes)"
+ - name: Upload new corpus entries
+ if: success() || failure()
+ # The forgejo/ fork, not the actions/ mirror: upstream's @actions/artifact
+ # client refuses to talk to any server that isn't github.com.
+ uses: https://data.forgejo.org/forgejo/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245 # v5
+ with:
+ name: hfuzz-corpus
+ path: new-corpus
+ compression-level: 0
+ if-no-files-found: ignore
+ # The nightly sweep deletes artifacts it has processed; the
+ # retention only has to bridge a missed nightly run.
+ retention-days: 2
linting:
runs-on: debian-trixie
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.