Switch to ldk-fuzzing-corpus repo rather than CI cache
What changed, and why it matters
This change only moves the project's fuzzing test data from a GitHub Actions cache into a separate public repository. It updates CI scripts to clone that repository, copy files into place, and open pull requests with any newly discovered fuzz inputs. There is no change to the actual Lightning Dev Kit code that users run, and nothing in the diff suggests a security vulnerability or fix.
No security action required. This is an infrastructure/maintenance change. Reviewers may optionally verify that the new workflow's permissions and token handling follow the repository's least-privilege practices, but the diff itself does not introduce a security issue.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors CI infrastructure for fuzzing: it replaces actions/cache restore/save steps with a git clone of lightningdevkit/ldk-fuzzing-corpus, symlinks/copies corpus directories into fuzz/hfuzz_workspace/*_target/input, and adds a new workflow (push-fuzz-corpus.yml) that runs on workflow_run to download the fuzz artifact and open a PR against the corpus repo. The workflow is explicitly designed to avoid executing PR-supplied code and only manipulates opaque corpus blobs. No application code, cryptographic logic, or network handling is modified.
Changed components
.github/workflows/build.yml.github/workflows/push-fuzz-corpus.ymlInspect captured patch +148 / −30
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1c34d8b..2cad565 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -82,11 +82,17 @@ jobs:
# Maybe if codecov wasn't broken we wouldn't need to do this...
./codecov --verbose upload-process --disable-search --fail-on-error -f target/codecov.json -t "f421b687-4dc2-4387-ac3d-dc3b2528af57" -F 'tests'
cargo clean
- - name: Download honggfuzz corpus
- uses: actions/download-artifact@v4
- with:
- name: hfuzz-corpus
- path: fuzz/hfuzz_workspace
+ - name: Clone fuzzing corpus
+ run: git clone --depth=1 https://github.com/lightningdevkit/ldk-fuzzing-corpus.git fuzz/ldk-fuzzing-corpus
+ - name: Symlink corpus into hfuzz_workspace
+ run: |
+ set -eu
+ cd fuzz
+ for D in ldk-fuzzing-corpus/rust-lightning/*/; do
+ NAME=$(basename "$D")
+ mkdir -p "hfuzz_workspace/${NAME}_target"
+ cp -r "ldk-fuzzing-corpus/rust-lightning/${NAME}" "hfuzz_workspace/${NAME}_target/input"
+ done
- name: Run fuzz coverage generation
run: |
./contrib/generate_fuzz_coverage.sh --output-dir `pwd` --output-codecov-json
@@ -233,39 +239,59 @@ jobs:
- name: Install Rust ${{ env.TOOLCHAIN }} toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain ${{ env.TOOLCHAIN }}
- # This is read-only for PRs. It seeds the fuzzer for a more effective run.
- # NOTE: The `key` is unique and will always miss, forcing a fallback to
- # the `restore-keys` to find the latest global cache from the `main` branch.
- - name: Restore persistent fuzz corpus (PR)
- if: ${{ github.ref != 'refs/heads/main' }}
- uses: actions/cache/restore@v4
- with:
- path: fuzz/hfuzz_workspace
- key: fuzz-corpus-${{ github.ref }}-${{ github.sha }}
- restore-keys: |
- fuzz-corpus-refs/heads/main-
- # The `restore-keys` performs a prefix search to find the most recent
- # cache from a previous `main` run. We then save with a new, unique
- # `key` (using the SHA) to ensure the cache is always updated,
- # as caches are immutable.
- - name: Restore/Save persistent honggfuzz corpus (Main)
- if: ${{ github.ref == 'refs/heads/main' }}
- uses: actions/cache@v4
- with:
- path: fuzz/hfuzz_workspace
- key: fuzz-corpus-refs/heads/main-${{ github.sha }}
- restore-keys: |
- fuzz-corpus-refs/heads/main-
+ - name: Clone fuzzing corpus
+ run: git clone --depth=1 https://github.com/lightningdevkit/ldk-fuzzing-corpus.git fuzz/ldk-fuzzing-corpus
+ - name: Symlink corpus into hfuzz_workspace
+ run: |
+ set -eu
+ cd fuzz
+ for D in ldk-fuzzing-corpus/rust-lightning/*/; do
+ NAME=$(basename "$D")
+ mkdir -p "hfuzz_workspace/${NAME}_target"
+ ln -sfn "../../ldk-fuzzing-corpus/rust-lightning/${NAME}" \
+ "hfuzz_workspace/${NAME}_target/input"
+ done
- name: Run fuzzers
run: cd fuzz && ./ci-fuzz.sh && cd ..
env:
FUZZ_MINIMIZE: ${{ contains(github.event.pull_request.labels.*.name, 'fuzz-minimize') }}
- - name: Upload honggfuzz corpus
+ - name: Stage new corpus entries for upload
+ if: success() || failure()
+ run: |
+ set -eu
+ WORKSPACE="$(pwd)"
+ rm -rf "$WORKSPACE/new-corpus"
+ mkdir -p "$WORKSPACE/new-corpus"
+
+ cd fuzz/ldk-fuzzing-corpus
+ 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}"
+ [ -d "$WORKSPACE/new-corpus/$NAME" ] || continue
+ for F in "$D"/SIG*; do
+ FILE="$(basename "$F")"
+ [ -f "$F" -a ! -f "$WORKSPACE/new-corpus/$NAME/$FILE" ] &&
+ cp "$F" "$WORKSPACE/new-corpus/$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()
uses: actions/upload-artifact@v4
with:
name: hfuzz-corpus
- path: fuzz/hfuzz_workspace
+ path: new-corpus
compression-level: 0
+ if-no-files-found: ignore
linting:
runs-on: ubuntu-latest
diff --git a/.github/workflows/push-fuzz-corpus.yml b/.github/workflows/push-fuzz-corpus.yml
new file mode 100644
index 0000000..4551de1
--- /dev/null
+++ b/.github/workflows/push-fuzz-corpus.yml
@@ -0,0 +1,92 @@
+name: Push fuzz corpus
+
+# Triggered after the main CI workflow finishes. Because `workflow_run` always
+# runs in the *base* repo's context (its workflow file as of `main`, with
+# full secrets access) it's safe to handle the corpus push here even for fork
+# PRs — none of the PR's modified code or scripts execute in this job.
+#
+# Caveat: GitHub only fires `workflow_run` for workflow files that live on
+# the default branch, so this workflow does nothing until it's merged to
+# `master`.
+on:
+ # zizmor flags `workflow_run` as a dangerous trigger because it runs with
+ # repo secrets in base-branch context. That's exactly why we use it here:
+ # this workflow never touches any PR-supplied code (no checkout, no script
+ # execution from the artifact — just cp/git on opaque corpus blobs), so
+ # the warning is a false positive.
+ workflow_run: # zizmor: ignore[dangerous-triggers]
+ workflows: ["Continuous Integration Checks"]
+ types: [completed]
+
+permissions:
+ # download-artifact across runs requires `actions: read`.
+ actions: read
+
+jobs:
+ push-corpus:
+ # Run on either success or fuzzer crash; skip on cancellation.
+ if: >-
+ github.event.workflow_run.conclusion == 'success' ||
+ github.event.workflow_run.conclusion == 'failure'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download fuzz corpus artifact
+ id: download
+ # The artifact only exists when the fuzz job got far enough to upload
+ # it. Don't fail this workflow if the upload was skipped.
+ continue-on-error: true
+ uses: actions/download-artifact@v4
+ with:
+ name: hfuzz-corpus
+ path: hfuzz-corpus
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Clone fuzzing corpus
+ if: steps.download.outcome == 'success'
+ run: git clone --depth=1 https://github.com/lightningdevkit/ldk-fuzzing-corpus.git
+
+ - name: Copy new corpus entries into the corpus checkout
+ if: steps.download.outcome == 'success'
+ run: |
+ set -eu
+ if [ -d hfuzz-corpus/rust-lightning ]; then
+ cp -rn hfuzz-corpus/rust-lightning/. ldk-fuzzing-corpus/rust-lightning/
+ fi
+
+ - name: Open PR with new corpus entries
+ if: steps.download.outcome == 'success'
+ env:
+ GH_TOKEN: ${{ secrets.CORPUS_PUSH_TOKEN }}
+ SOURCE_SHA: ${{ github.event.workflow_run.head_sha }}
+ RUN_URL: ${{ github.event.workflow_run.html_url }}
+ RUN_ID: ${{ github.event.workflow_run.id }}
+ run: |
+ set -eu
+ cd 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
Why this scored 14/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.