Skip to content

supply-chain: promote cargo-deny gate to advisories+bans+sources; run… #25

supply-chain: promote cargo-deny gate to advisories+bans+sources; run…

supply-chain: promote cargo-deny gate to advisories+bans+sources; run… #25

Workflow file for this run

name: CI
# Born-green CI for PQSigner. Each job runs a command verified to pass on the
# current tree (2026-06-16). The two pre-existing reds tracked in earlier
# revisions of this file are now FIXED and GATED here (docs/work-todo.md §34):
# * usb_hw raw-MMIO source-pin (commit 4d1d6476 — raw read/write_volatile
# migrated to hw::mmio::Reg32). Gated by the `secure-tests` job (the full
# sphincs-tz-secure host suite: 2076 passed / 0 failed on this tree).
# * The `contracts` job was dying at COMPILE time in CI: the Foundry libs
# (forge-std, solady, account-abstraction, …) are git-pinned by
# contracts/smart-wallet/foundry.lock, but `lib/` is gitignored and they
# are NOT git submodules — so a clean CI checkout had nothing to compile
# against ("Unable to resolve imports"). The job now restores each lib at
# its EXACT foundry.lock rev, then runs the full default `forge test`
# (109 passed / 0 failed / 1 skipped — the skip is DeployedBytecodeReproCheck,
# which only runs under the deploy profile). The committed codehash pins
# (0xf1ef… verifier, 0x43c654… wallet) are canonical for exactly this
# foundry.lock lib set; an extra/missing lib perturbs solc metadata and
# therefore the codehash, so the restore must be lib-exact.
#
# Intentionally NOT in CI (kept as local/manual gates — slow + need the
# patched halmos toolchain): the `deploy` foundry profile (runs=999999) and
# `make -C contracts/verification verify-bytecode` (the symbolic discharge).
# Under the deploy profile DeployedBytecodeReproCheck reproduces the on-chain
# Base Mainnet bytecode exactly (verified locally in a clean lib-exact tree).
on:
push:
branches: [master]
# Skip CI when a push touches ONLY docs / proofs / formal-models — none of
# these feed any CI job (the verify-bytecode + kontrol gates are local-only,
# and contracts/verification/** is never compiled by the `contracts` job,
# which builds contracts/smart-wallet). A push that ALSO changes any other
# file still runs the full suite. Conserves private-repo Actions minutes.
paths-ignore:
- '**/*.md'
- 'docs/**'
- '**/*.lean'
- 'contracts/verification/**'
pull_request:
paths-ignore:
- '**/*.md'
- 'docs/**'
- '**/*.lean'
- 'contracts/verification/**'
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
invariant-gates:
name: Invariant gates (#5 / #6 / #7)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Deterministic supply-chain gate (fail-hard):
# * bans — invariant #5: no classical-signer crate (deny.toml [bans])
# * advisories — real CVEs in any dep (unmaintained is workspace-scoped per
# deny.toml so transitive-unmaintained deps don't tripwire CI)
# * sources — deps only from expected registries/remotes (typosquat /
# dependency-confusion guard)
# Licenses are deliberately NOT a hard gate (a compliance tripwire, not a
# security property) — captured by the SBOM (`make sbom`) instead.
- name: cargo deny check (advisories + bans + sources)
uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check advisories bans sources
# Invariants #5/#6/#7 (source level): the hard ERROR rules must be clean.
- name: semgrep invariant rules (ERROR-level)
run: |
python3 -m pip install --quiet semgrep
semgrep --config .semgrep/pqsigner-invariants.yml --severity ERROR --error --metrics off
host-tests:
name: Host unit tests (pure-logic crates)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: cargo test (pure-logic crates)
run: |
cargo test --locked --lib \
-p pqsigner-proto -p pqsigner-tx-core -p pqsigner-aa \
-p pqsigner-domain -p pqsigner-tx -p pqsigner-erc7730 \
-p sphincs-c10 -p pqsigner-fi
secure-tests:
name: Secure-world unit tests (host)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# The secure crate host-builds with --no-default-features (the
# semihosting backend is arch-gated to a host stub). This is the
# `make test-unit` secure path; gates the usb_hw raw-MMIO fix.
- name: cargo test (sphincs-tz-secure, host)
run: |
cargo test --locked -p sphincs-tz-secure \
--no-default-features \
--features mock-se,debug-log,ui-semihosting
contracts:
name: Contracts (forge test)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: foundry-rs/foundry-toolchain@v1
# lib/ is gitignored and the deps are git-pinned by foundry.lock (NOT
# submodules), so restore each at its EXACT locked rev. The codehash
# freeze tests are lib-exact: an extra/missing lib changes the
# auto-generated remappings, which solc folds into the metadata hash
# appended to the runtime bytecode → a different codehash.
- name: Restore pinned Foundry libs (foundry.lock revs)
working-directory: contracts/smart-wallet
run: |
set -euo pipefail
declare -A URL=(
[forge-std]=https://github.com/foundry-rs/forge-std
[solady]=https://github.com/vectorized/solady
[account-abstraction]=https://github.com/eth-infinitism/account-abstraction
[openzeppelin-contracts]=https://github.com/openzeppelin/openzeppelin-contracts
[p256-verifier]=https://github.com/daimo-eth/p256-verifier
[safe-singleton-deployer-sol]=https://github.com/wilsoncusack/safe-singleton-deployer-sol
[webauthn-sol]=https://github.com/base-org/webauthn-sol
)
mkdir -p lib
for key in $(jq -r 'keys[]' foundry.lock); do
name="${key#lib/}"
url="${URL[$name]:-}"
if [ -z "$url" ]; then
echo "::error::foundry.lock dep '$name' has no URL mapping in ci.yml — add it"; exit 1
fi
rev=$(jq -r --arg k "$key" '.[$k].rev' foundry.lock)
git clone -q "$url" "lib/$name"
git -C "lib/$name" -c advice.detachedHead=false checkout -q "$rev"
echo " restored $name @ $rev"
done
- name: forge test (default profile)
working-directory: contracts/smart-wallet
run: forge test