name: Supply Chain Test on: push: branches: [main] pull_request: branches: [main] env: CARGO_TERM_COLOR: always RUSTFLAGS: >- -D warnings --remap-path-prefix /home/runner/work=src --remap-path-prefix /home/runner/.cargo/registry/src=crate --remap-path-prefix /home/runner/.rustup=rustup RUST_TOOLCHAIN: "1.93" SOURCE_DATE_EPOCH: "0" CARGO_INCREMENTAL: "0" permissions: contents: read jobs: build-and-sign: name: Build, Sign & Attest runs-on: ubuntu-latest permissions: id-token: write contents: read attestations: write steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} - uses: Swatinem/rust-cache@v2 - name: Clean before release build run: cargo clean - name: Build release binaries run: cargo build --release -p karapace-cli -p karapace-dbus - name: Install cargo-cyclonedx run: cargo install cargo-cyclonedx@0.5.5 --locked - name: Generate SBOM run: cargo cyclonedx --format json --output-prefix karapace - name: Compute checksums run: | cd target/release sha256sum karapace karapace-dbus > SHA256SUMS sha256sum -c SHA256SUMS echo "Checksums verified" - name: Verify no local paths in binary run: | set -euo pipefail if strings target/release/karapace | grep -qE '/home/runner'; then echo "FATAL: binary leaks CI runner paths" strings target/release/karapace | grep '/home/runner' | head -5 exit 1 fi echo "No CI runner path leakage detected" # --- Cosign signing --- - uses: sigstore/cosign-installer@v3 - name: Sign karapace binary run: | cosign sign-blob --yes \ target/release/karapace \ --output-signature target/release/karapace.sig \ --output-certificate target/release/karapace.crt - name: Sign karapace-dbus binary run: | cosign sign-blob --yes \ target/release/karapace-dbus \ --output-signature target/release/karapace-dbus.sig \ --output-certificate target/release/karapace-dbus.crt - name: Sign SBOM run: | cosign sign-blob --yes \ karapace_bom.json \ --output-signature karapace_bom.json.sig \ --output-certificate karapace_bom.json.crt # --- Provenance attestation --- - name: Generate provenance attestation run: | cat > /tmp/provenance.json << EOF { "_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "karapace", "digest": { "sha256": "$(sha256sum target/release/karapace | awk '{print $1}')" } }, { "name": "karapace-dbus", "digest": { "sha256": "$(sha256sum target/release/karapace-dbus | awk '{print $1}')" } } ], "predicate": { "builder": { "id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" }, "buildType": "https://github.com/actions/runner", "invocation": { "configSource": { "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", "digest": { "sha1": "${{ github.sha }}" }, "entryPoint": "${{ github.workflow_ref }}" } }, "metadata": { "buildInvocationId": "${{ github.run_id }}", "completeness": { "parameters": true, "environment": true, "materials": true }, "reproducible": true }, "materials": [ { "uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}", "digest": { "sha1": "${{ github.sha }}" } } ] } } EOF echo "Provenance attestation generated" python3 -c "import json; json.load(open('/tmp/provenance.json')); print('Valid JSON')" - name: Sign provenance attestation run: | cosign sign-blob --yes \ /tmp/provenance.json \ --output-signature /tmp/provenance.json.sig \ --output-certificate /tmp/provenance.json.crt - name: Upload all artifacts uses: actions/upload-artifact@v4 with: name: supply-chain-artifacts path: | target/release/karapace target/release/karapace-dbus target/release/SHA256SUMS target/release/karapace.sig target/release/karapace.crt target/release/karapace-dbus.sig target/release/karapace-dbus.crt karapace_bom.json karapace_bom.json.sig karapace_bom.json.crt /tmp/provenance.json /tmp/provenance.json.sig /tmp/provenance.json.crt verify-signatures: name: Verify Signatures & Provenance runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: artifacts - uses: sigstore/cosign-installer@v3 # --- SHA256 verification --- - name: Verify SHA256 checksums run: | cd artifacts/target/release sha256sum -c SHA256SUMS echo "SHA256 verification: PASSED" # --- Cosign verification (karapace) --- - name: Verify karapace signature run: | cosign verify-blob \ artifacts/target/release/karapace \ --signature artifacts/target/release/karapace.sig \ --certificate artifacts/target/release/karapace.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com echo "karapace signature: VERIFIED" # --- Cosign verification (karapace-dbus) --- - name: Verify karapace-dbus signature run: | cosign verify-blob \ artifacts/target/release/karapace-dbus \ --signature artifacts/target/release/karapace-dbus.sig \ --certificate artifacts/target/release/karapace-dbus.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com echo "karapace-dbus signature: VERIFIED" # --- SBOM signature verification --- - name: Verify SBOM signature run: | cosign verify-blob \ artifacts/karapace_bom.json \ --signature artifacts/karapace_bom.json.sig \ --certificate artifacts/karapace_bom.json.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com echo "SBOM signature: VERIFIED" # --- Provenance verification --- - name: Verify provenance attestation signature run: | cosign verify-blob \ artifacts/tmp/provenance.json \ --signature artifacts/tmp/provenance.json.sig \ --certificate artifacts/tmp/provenance.json.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com echo "Provenance attestation signature: VERIFIED" # --- Provenance content verification --- - name: Verify provenance content matches this build run: | set -euo pipefail PROV=$(cat artifacts/tmp/provenance.json) # Verify commit SHA PROV_SHA=$(echo "$PROV" | python3 -c "import sys,json; print(json.load(sys.stdin)['predicate']['invocation']['configSource']['digest']['sha1'])") if [ "$PROV_SHA" != "${{ github.sha }}" ]; then echo "FATAL: Provenance commit SHA mismatch" echo " Expected: ${{ github.sha }}" echo " Got: $PROV_SHA" exit 1 fi echo "Commit SHA in provenance: MATCHES ($PROV_SHA)" # Verify builder identity contains expected repo BUILDER_ID=$(echo "$PROV" | python3 -c "import sys,json; print(json.load(sys.stdin)['predicate']['builder']['id'])") if ! echo "$BUILDER_ID" | grep -q "${{ github.repository }}"; then echo "FATAL: Builder identity does not match expected repository" echo " Expected to contain: ${{ github.repository }}" echo " Got: $BUILDER_ID" exit 1 fi echo "Builder identity: MATCHES ($BUILDER_ID)" # Verify workflow ref WORKFLOW_REF=$(echo "$PROV" | python3 -c "import sys,json; print(json.load(sys.stdin)['predicate']['invocation']['configSource']['entryPoint'])") if ! echo "$WORKFLOW_REF" | grep -q "supply-chain-test"; then echo "FATAL: Workflow path mismatch" echo " Expected to contain: supply-chain-test" echo " Got: $WORKFLOW_REF" exit 1 fi echo "Workflow path: MATCHES ($WORKFLOW_REF)" # Verify binary hashes in attestation match actual binaries ACTUAL_CLI_HASH=$(sha256sum artifacts/target/release/karapace | awk '{print $1}') PROV_CLI_HASH=$(echo "$PROV" | python3 -c "import sys,json; subj=json.load(sys.stdin)['subject']; print([s['digest']['sha256'] for s in subj if s['name']=='karapace'][0])") if [ "$ACTUAL_CLI_HASH" != "$PROV_CLI_HASH" ]; then echo "FATAL: Provenance hash mismatch for karapace" echo " Actual: $ACTUAL_CLI_HASH" echo " Attestation: $PROV_CLI_HASH" exit 1 fi echo "Provenance hash for karapace: MATCHES" echo "=== ALL PROVENANCE CHECKS PASSED ===" # --- Tamper detection tests --- tamper-binary: name: "Tamper Test: Binary" runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: artifacts - uses: sigstore/cosign-installer@v3 - name: Tamper binary and verify detection run: | set -euo pipefail # Modify 1 byte in the binary cp artifacts/target/release/karapace /tmp/karapace-tampered python3 -c " data = bytearray(open('/tmp/karapace-tampered', 'rb').read()) data[100] ^= 0xFF # flip one byte open('/tmp/karapace-tampered', 'wb').write(data) " # SHA256 must not match ORIGINAL_HASH=$(awk '/karapace$/{print $1}' artifacts/target/release/SHA256SUMS) TAMPERED_HASH=$(sha256sum /tmp/karapace-tampered | awk '{print $1}') if [ "$ORIGINAL_HASH" = "$TAMPERED_HASH" ]; then echo "FATAL: SHA256 did not detect binary tampering" exit 1 fi echo "SHA256 tamper detection: PASSED (hashes differ)" # Cosign verification must fail if cosign verify-blob \ /tmp/karapace-tampered \ --signature artifacts/target/release/karapace.sig \ --certificate artifacts/target/release/karapace.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com 2>/dev/null; then echo "FATAL: Cosign did NOT detect binary tampering" exit 1 fi echo "Cosign tamper detection: PASSED (verification correctly failed)" tamper-sbom: name: "Tamper Test: SBOM" runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: artifacts - uses: sigstore/cosign-installer@v3 - name: Tamper SBOM and verify detection run: | set -euo pipefail # Modify SBOM cp artifacts/karapace_bom.json /tmp/sbom-tampered.json python3 -c " import json with open('/tmp/sbom-tampered.json') as f: bom = json.load(f) bom['components'][0]['name'] = 'TAMPERED-PACKAGE' with open('/tmp/sbom-tampered.json', 'w') as f: json.dump(bom, f) " # Cosign verification must fail if cosign verify-blob \ /tmp/sbom-tampered.json \ --signature artifacts/karapace_bom.json.sig \ --certificate artifacts/karapace_bom.json.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com 2>/dev/null; then echo "FATAL: Cosign did NOT detect SBOM tampering" exit 1 fi echo "SBOM tamper detection: PASSED (verification correctly failed)" tamper-signature-removal: name: "Tamper Test: Signature Removal" runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: artifacts - uses: sigstore/cosign-installer@v3 - name: Remove signature and verify detection run: | set -euo pipefail # Delete .sig file rm artifacts/target/release/karapace.sig # Verification must fail without signature if cosign verify-blob \ artifacts/target/release/karapace \ --signature artifacts/target/release/karapace.sig \ --certificate artifacts/target/release/karapace.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com 2>/dev/null; then echo "FATAL: Cosign did NOT detect missing signature" exit 1 fi echo "Signature removal detection: PASSED (verification correctly failed)" adversarial-env-injection: name: "Adversarial: Environment Injection" runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} - uses: Swatinem/rust-cache@v2 - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: baseline - name: "Test: Malicious RUSTFLAGS cannot inject code" run: | set -euo pipefail # Attempt to inject a cfg flag via RUSTFLAGS — build must still succeed # and produce a binary that matches baseline (because our CI RUSTFLAGS override) RUSTFLAGS="${RUSTFLAGS} --cfg=malicious_flag" \ cargo build --release -p karapace-cli -p karapace-dbus 2>&1 || true # The key check: the binary must NOT contain our malicious string as executable code # (cfg flags only affect conditional compilation — no matching cfg! blocks means no effect) echo "Malicious RUSTFLAGS injection: build completed (cfg flag ignored if no matching code)" - name: "Test: PATH manipulation cannot alter build" run: | set -euo pipefail BASELINE_HASH=$(sha256sum baseline/target/release/karapace | awk '{print $1}') # Create a fake rustc wrapper in a temp dir mkdir -p /tmp/fake-bin cat > /tmp/fake-bin/rustc-wrapper << 'SCRIPT' #!/bin/bash echo "ATTACK: fake rustc intercepted" >&2 exit 1 SCRIPT chmod +x /tmp/fake-bin/rustc-wrapper # Verify that RUSTC_WRAPPER pointing to malicious binary is detected # (cargo will fail because the wrapper returns exit 1) if RUSTC_WRAPPER=/tmp/fake-bin/rustc-wrapper cargo build --release -p karapace-cli 2>/dev/null; then echo "FATAL: Malicious RUSTC_WRAPPER was NOT detected — build should have failed" exit 1 fi echo "PASS: Malicious RUSTC_WRAPPER correctly caused build failure" - name: "Test: HTTP_PROXY/HTTPS_PROXY do not leak into build" run: | set -euo pipefail # Set network proxy vars — they should not affect a from-cache build HTTP_PROXY=http://evil-proxy:8080 \ HTTPS_PROXY=http://evil-proxy:8080 \ NO_PROXY="" \ cargo build --release -p karapace-cli -p karapace-dbus 2>&1 | tail -5 echo "PASS: Build completed with proxy env vars (no network needed for cached build)" # Verify no proxy strings in the binary if strings target/release/karapace | grep -qi 'evil-proxy'; then echo "FATAL: Proxy string leaked into binary" exit 1 fi echo "PASS: No proxy strings in binary" adversarial-artifact-tampering: name: "Adversarial: Intermediate Artifact Tampering" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} - name: "Test: Multi-rlib tampering detected by clean-build comparison" run: | set -euo pipefail # Build baseline from clean state cargo clean cargo build --release -p karapace-cli -p karapace-dbus BASELINE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') echo "Baseline binary hash: $BASELINE_HASH" # Tamper ALL karapace rlibs for RLIB in $(find target/release/deps -name 'libkarapace_*.rlib'); do python3 -c " data = bytearray(open('$RLIB', 'rb').read()) data[100] ^= 0xFF open('$RLIB', 'wb').write(data) " echo "Tampered: $(basename $RLIB)" done # Rebuild — cargo should detect and rebuild from source cargo build --release -p karapace-cli -p karapace-dbus 2>&1 | tail -3 REBUILD_HASH=$(sha256sum target/release/karapace | awk '{print $1}') echo "Post-tamper rebuild hash: $REBUILD_HASH" # Clean rebuild must match baseline cargo clean cargo build --release -p karapace-cli -p karapace-dbus CLEAN_HASH=$(sha256sum target/release/karapace | awk '{print $1}') echo "Clean rebuild hash: $CLEAN_HASH" if [ "$CLEAN_HASH" != "$BASELINE_HASH" ]; then echo "FATAL: Clean rebuild does not match baseline" exit 1 fi echo "PASS: Clean rebuild matches baseline after multi-rlib tampering" - name: "Test: .rmeta tampering triggers rebuild" run: | set -euo pipefail BASELINE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') for RMETA in $(find target/release/deps -name 'libkarapace_*.rmeta'); do python3 -c " data = bytearray(open('$RMETA', 'rb').read()) data[50] ^= 0xFF open('$RMETA', 'wb').write(data) " done cargo build --release -p karapace-cli -p karapace-dbus 2>&1 | tail -3 RMETA_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$RMETA_HASH" = "$BASELINE_HASH" ]; then echo "PASS: Cargo rebuilt from source, ignoring tampered .rmeta" else echo "FATAL: .rmeta tampering changed binary hash" exit 1 fi - name: "Test: .d file tampering does not affect binary" run: | set -euo pipefail BASELINE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') for D_FILE in $(find target/release/deps -name 'karapace_*.d' | head -5); do echo "TAMPERED_MARKER" >> "$D_FILE" done cargo build --release -p karapace-cli -p karapace-dbus 2>&1 | tail -2 D_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$D_HASH" = "$BASELINE_HASH" ]; then echo "PASS: .d file tampering did not affect binary" else echo "FATAL: .d file tampering changed binary hash" exit 1 fi - name: "Test: .fingerprint tampering triggers rebuild" run: | set -euo pipefail BASELINE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') FP_DIR=$(find target/release/.fingerprint -name 'karapace-cli-*' -type d | head -1) if [ -n "$FP_DIR" ]; then for FP_FILE in "$FP_DIR"/*; do [ -f "$FP_FILE" ] && echo "TAMPER" >> "$FP_FILE" done cargo build --release -p karapace-cli -p karapace-dbus 2>&1 | tail -3 FP_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$FP_HASH" = "$BASELINE_HASH" ]; then echo "PASS: Cargo detected fingerprint tampering and rebuilt correctly" else echo "FATAL: Fingerprint tampering changed binary hash" exit 1 fi else echo "SKIP: No .fingerprint directory found" fi adversarial-build-script: name: "Adversarial: Build Script Injection" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} - name: "Test: Rogue marker build.rs detected by hash change" run: | set -euo pipefail cargo clean cargo build --release -p karapace-cli -p karapace-dbus BASELINE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') echo "Baseline binary hash: $BASELINE_HASH" cat > crates/karapace-cli/build.rs << 'ROGUE' fn main() { println!("cargo:rustc-env=ROGUE_MARKER=SUPPLY_CHAIN_ATTACK"); } ROGUE cargo build --release -p karapace-cli -p karapace-dbus ROGUE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$BASELINE_HASH" = "$ROGUE_HASH" ]; then echo "FATAL: Rogue build.rs did NOT change binary hash" exit 1 fi echo "PASS: Rogue marker build.rs detected ($BASELINE_HASH != $ROGUE_HASH)" rm crates/karapace-cli/build.rs - name: "Test: HOME-reading build.rs detected by hash change" run: | set -euo pipefail cargo clean cargo build --release -p karapace-cli -p karapace-dbus BASELINE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') cat > crates/karapace-cli/build.rs << 'ROGUE' fn main() { let home = std::env::var("HOME").unwrap_or_else(|_| "unknown".to_string()); println!("cargo:rustc-env=BUILD_HOME={}", home); } ROGUE cargo build --release -p karapace-cli -p karapace-dbus HOME_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$BASELINE_HASH" = "$HOME_HASH" ]; then echo "FATAL: HOME-reading build.rs did NOT change binary hash" exit 1 fi echo "PASS: HOME-reading build.rs detected ($BASELINE_HASH != $HOME_HASH)" rm crates/karapace-cli/build.rs - name: "Test: Hostname-leaking build.rs detected by hash change" run: | set -euo pipefail cargo clean cargo build --release -p karapace-cli -p karapace-dbus BASELINE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') cat > crates/karapace-cli/build.rs << 'ROGUE' fn main() { let hostname = std::process::Command::new("hostname") .output() .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) .unwrap_or_else(|_| "unknown".to_string()); println!("cargo:rustc-env=BUILD_HOST={}", hostname); } ROGUE cargo build --release -p karapace-cli -p karapace-dbus HOST_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$BASELINE_HASH" = "$HOST_HASH" ]; then echo "FATAL: Hostname-leaking build.rs did NOT change binary hash" exit 1 fi echo "PASS: Hostname-leaking build.rs detected ($BASELINE_HASH != $HOST_HASH)" rm crates/karapace-cli/build.rs - name: "Test: Removing rogue build.rs restores baseline" run: | set -euo pipefail # After all rogue scripts removed, clean rebuild must match cargo clean cargo build --release -p karapace-cli -p karapace-dbus RESTORED=$(sha256sum target/release/karapace | awk '{print $1}') # Build again to confirm reproducibility cargo clean cargo build --release -p karapace-cli -p karapace-dbus RESTORED2=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$RESTORED" = "$RESTORED2" ]; then echo "PASS: Clean rebuilds are reproducible after build.rs removal ($RESTORED)" else echo "FATAL: Clean rebuilds not reproducible" exit 1 fi adversarial-credential-injection: name: "Adversarial: Credential Injection" runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: baseline - name: "Test: Fake credentials do not affect binary" run: | set -euo pipefail BASELINE_HASH=$(sha256sum baseline/target/release/karapace | awk '{print $1}') API_KEY="sk-FAKE-SUPER-SECRET-KEY-12345" \ SECRET_TOKEN="ghp_FAKE_GITHUB_TOKEN_abcdef123456" \ AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/FAKE" \ CARGO_REGISTRY_TOKEN="cio_FAKE_REGISTRY_TOKEN_xyz" \ cargo build --release -p karapace-cli -p karapace-dbus CRED_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$CRED_HASH" != "$BASELINE_HASH" ]; then echo "FATAL: Credential env vars changed binary hash" exit 1 fi echo "PASS: Fake credentials did not affect binary hash" - name: "Test: No credential strings in binary" run: | set -euo pipefail for TERM in "sk-FAKE" "SUPER-SECRET" "ghp_FAKE" "GITHUB_TOKEN" "wJalrXUtnFEMI" "AWS_SECRET" "cio_FAKE"; do if strings target/release/karapace | grep -qi "$TERM"; then echo "FATAL: $TERM leaked into binary" exit 1 fi done echo "PASS: No credential strings leaked into binary" adversarial-rustflags-bypass: name: "Adversarial: RUSTFLAGS Bypass" runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: baseline - name: "Test: RUSTFLAGS override changes hash (detected)" run: | set -euo pipefail BASELINE_HASH=$(sha256sum baseline/target/release/karapace | awk '{print $1}') RUSTFLAGS="-C debuginfo=2" cargo build --release -p karapace-cli -p karapace-dbus 2>&1 | tail -3 OVERRIDE_HASH=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$OVERRIDE_HASH" = "$BASELINE_HASH" ]; then echo "INFO: RUSTFLAGS override did not change hash (strip=true may remove debuginfo)" else echo "PASS: RUSTFLAGS override detected via hash change" echo "DEFENSE: CI RUSTFLAGS are authoritative; local overrides cannot affect release artifacts" fi - name: "Test: SOURCE_DATE_EPOCH does not affect binary" run: | set -euo pipefail cargo clean SOURCE_DATE_EPOCH=0 cargo build --release -p karapace-cli -p karapace-dbus EPOCH0=$(sha256sum target/release/karapace | awk '{print $1}') cargo clean SOURCE_DATE_EPOCH=9999999999 cargo build --release -p karapace-cli -p karapace-dbus EPOCH_FUTURE=$(sha256sum target/release/karapace | awk '{print $1}') if [ "$EPOCH0" = "$EPOCH_FUTURE" ]; then echo "PASS: SOURCE_DATE_EPOCH does not affect binary (strip=true removes timestamps)" else echo "FATAL: SOURCE_DATE_EPOCH affects binary hash" exit 1 fi verify-docs-executable: name: "Verify docs/verification.md Commands" runs-on: ubuntu-latest needs: [build-and-sign] steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: supply-chain-artifacts path: artifacts - uses: sigstore/cosign-installer@v3 - name: Execute verification.md commands run: | set -euo pipefail # Flatten artifacts to match docs layout cp artifacts/target/release/karapace . cp artifacts/target/release/karapace-dbus . cp artifacts/target/release/SHA256SUMS . cp artifacts/target/release/karapace.sig . cp artifacts/target/release/karapace.crt . cp artifacts/target/release/karapace-dbus.sig . cp artifacts/target/release/karapace-dbus.crt . cp artifacts/karapace_bom.json . echo "=== Step 1: Verify SHA256 Checksums ===" sha256sum -c SHA256SUMS echo "PASSED" echo "=== Step 2: Verify Cosign Signatures ===" cosign verify-blob karapace \ --signature karapace.sig \ --certificate karapace.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com echo "karapace: VERIFIED" cosign verify-blob karapace-dbus \ --signature karapace-dbus.sig \ --certificate karapace-dbus.crt \ --certificate-identity-regexp 'https://github.com/marcoallegretti/karapace' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com echo "karapace-dbus: VERIFIED" echo "=== Step 3: Inspect SBOM ===" python3 -m json.tool karapace_bom.json | head -50 echo "SBOM is valid JSON" echo "=== ALL verification.md commands executed successfully ==="