Repository navigation
chore(main): release cachekit-core 0.3.0 #283
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Security | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| # Saturday 11:07 UTC = Sat 21:07 AEST / 22:07 AEDT (Sydney night, year-round). | |
| # Weekly cadence: deep fuzz at 1h/target × 16 targets is ~16 runner-hours, which | |
| # drains in a few hours overnight instead of monopolising the shared ARC pool for | |
| # ~32h. The full 8h/target run is opt-in via workflow_dispatch (run_deep_fuzz). | |
| # PR-time coverage (cargo audit/deny, Cargo Vet, Quick Fuzz, CodeQL) catches | |
| # regressions promptly; deep fuzz is for finding bugs, not gating merges. | |
| # Off-minute (:07) avoids the cron pile-up that GitHub schedules at :00. | |
| - cron: '7 11 * * 6' | |
| release: | |
| types: [published] | |
| # On-demand: lets the schedule-only jobs (e.g. Kani) be run and verified | |
| # without waiting for the weekly cron. A plain dispatch does NOT trigger the | |
| # heavy deep-fuzz matrix — set run_deep_fuzz=true to opt into that. | |
| workflow_dispatch: | |
| inputs: | |
| run_deep_fuzz: | |
| description: "Run the full deep-fuzz matrix (heavy — occupies the ARC pool)" | |
| type: boolean | |
| default: false | |
| fuzz_seconds: | |
| description: "Seconds per target for an on-demand deep fuzz (default 8h)" | |
| type: string | |
| default: "28800" | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| jobs: | |
| fast-security: | |
| name: Fast Security Checks | |
| runs-on: cachekit | |
| if: github.event_name == 'push' || github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: "stable" | |
| components: clippy | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-security-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-security- | |
| ${{ runner.os }}-cargo- | |
| - name: Install cargo-audit | |
| run: cargo install cargo-audit --locked | |
| - name: Install cargo-deny | |
| run: cargo install cargo-deny --locked | |
| - name: Run cargo audit (CVE scanning) | |
| run: cargo audit | |
| - name: Run cargo deny (license compliance + advisories) | |
| run: cargo deny check | |
| - name: Run clippy (strict linting) | |
| run: cargo clippy --all-features --all-targets -- -D warnings | |
| - name: Run tests | |
| run: cargo test --all-features | |
| quick-fuzz: | |
| name: Quick Fuzz (Corpus Only) | |
| runs-on: cachekit | |
| if: github.event_name == 'push' || github.event_name == 'pull_request' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - byte_storage_checksum_collision | |
| - byte_storage_compress | |
| - byte_storage_corrupted_envelope | |
| - byte_storage_decompress | |
| - byte_storage_empty_data | |
| - byte_storage_format_injection | |
| - byte_storage_integer_overflow | |
| - compression_bomb | |
| - encryption_aad_injection | |
| - encryption_key_derivation | |
| - encryption_large_payload | |
| - encryption_nonce_reuse | |
| - encryption_roundtrip | |
| - encryption_truncated_ciphertext | |
| - integration_layered_security | |
| - key_derivation | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| fuzz/target/ | |
| key: ${{ runner.os }}-cargo-fuzz-nightly-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-fuzz-nightly- | |
| ${{ runner.os }}-cargo- | |
| - name: Install cargo-fuzz | |
| # NOTE: --locked omitted: cargo-fuzz 0.13.1's published Cargo.lock pins | |
| # rustix 0.36.5, which uses unstable `rustc_attrs` cfg gates that newer | |
| # nightly rustc rejects. Let cargo resolve fresh deps. | |
| run: cargo install cargo-fuzz | |
| - name: Run quick fuzz (corpus only) | |
| run: | | |
| cd fuzz | |
| # Build first - fail fast on compile errors | |
| cargo fuzz build ${{ matrix.target }} | |
| # libFuzzer self-exits at -max_total_time (graceful "Done ..."); the outer | |
| # `timeout` is a +30s hang safety-net only. Buffer matters because libFuzzer's | |
| # clock starts after build/corpus-load, so an equal `timeout` would always | |
| # SIGTERM first ("run interrupted") and the graceful path would be unreachable. | |
| # Exit 124 (timeout fired = genuine hang) is still tolerated. | |
| timeout 150 cargo fuzz run ${{ matrix.target }} -- -runs=0 -max_total_time=120 || [ $? -eq 124 ] | |
| deep-fuzz: | |
| name: Deep Fuzzing | |
| runs-on: cachekit | |
| # Scheduled weekly run is light (1h/target) so it can't monopolise the shared | |
| # ARC pool. The full 8h/target run is opt-in via workflow_dispatch (run_deep_fuzz). | |
| if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.run_deep_fuzz) | |
| # 540min cap accommodates the on-demand 8h path; the inner timeout governs the | |
| # actual scheduled (1h) vs dispatched (configurable) duration. | |
| timeout-minutes: 540 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - byte_storage_checksum_collision | |
| - byte_storage_compress | |
| - byte_storage_corrupted_envelope | |
| - byte_storage_decompress | |
| - byte_storage_empty_data | |
| - byte_storage_format_injection | |
| - byte_storage_integer_overflow | |
| - compression_bomb | |
| - encryption_aad_injection | |
| - encryption_key_derivation | |
| - encryption_large_payload | |
| - encryption_nonce_reuse | |
| - encryption_roundtrip | |
| - encryption_truncated_ciphertext | |
| - integration_layered_security | |
| - key_derivation | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| fuzz/target/ | |
| key: ${{ runner.os }}-cargo-fuzz-nightly-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-fuzz-nightly- | |
| ${{ runner.os }}-cargo- | |
| - name: Install cargo-fuzz | |
| # NOTE: --locked omitted: cargo-fuzz 0.13.1's published Cargo.lock pins | |
| # rustix 0.36.5, which uses unstable `rustc_attrs` cfg gates that newer | |
| # nightly rustc rejects. Let cargo resolve fresh deps. | |
| run: cargo install cargo-fuzz | |
| - name: Run deep fuzz | |
| # Scheduled runs use 1h/target (keeps the ARC pool free for PR CI); a manual | |
| # workflow_dispatch can request the full 8h (or any duration) via fuzz_seconds. | |
| env: | |
| FUZZ_SECONDS: ${{ (github.event_name == 'workflow_dispatch' && inputs.fuzz_seconds) || '3600' }} | |
| run: | | |
| # Validate the dispatch-supplied duration before it reaches shell arithmetic | |
| # and the fuzzer. Must be a positive integer and within the 540min job cap. | |
| case "$FUZZ_SECONDS" in | |
| ''|*[!0-9]*) echo "::error::fuzz_seconds must be a positive integer (got '$FUZZ_SECONDS')"; exit 1 ;; | |
| esac | |
| if [ "$FUZZ_SECONDS" -lt 1 ] || [ "$FUZZ_SECONDS" -gt 32400 ]; then | |
| echo "::error::fuzz_seconds must be 1..32400 (<= 540min job cap), got $FUZZ_SECONDS"; exit 1 | |
| fi | |
| cd fuzz | |
| # Build first - fail fast on compile errors | |
| cargo fuzz build ${{ matrix.target }} | |
| # libFuzzer self-exits at -max_total_time with a graceful "Done N runs ..." | |
| # (final stats + corpus consolidation). The outer `timeout` is +3min slack so | |
| # it only fires on a genuine hang — NOT as the normal end-of-budget stop. An | |
| # equal `timeout` would always win the race (libFuzzer's clock starts later, | |
| # after build/corpus-load) and every run would log "run interrupted" instead, | |
| # making a real hang indistinguishable from normal completion. Exit 124 (hang | |
| # killed by timeout) remains tolerated. | |
| timeout "$((FUZZ_SECONDS + 180))" cargo fuzz run ${{ matrix.target }} -- -max_total_time="$FUZZ_SECONDS" || [ $? -eq 124 ] | |
| - name: Upload crash artifacts | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: fuzz-crashes-${{ matrix.target }} | |
| path: fuzz/artifacts/${{ matrix.target }}/ | |
| if-no-files-found: ignore | |
| kani: | |
| name: Kani Formal Verification | |
| runs-on: cachekit | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| permissions: | |
| contents: read # least-privilege: the job only checks out and verifies | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master | |
| with: | |
| # Host toolchain for the kani-verifier installer only — Kani downloads its | |
| # own pinned verification toolchain in `cargo kani setup`. Must be current | |
| # stable: kani-verifier's deps (e.g. home 0.5.12) now require rustc >= 1.88, | |
| # so the old "1.85" pin failed to compile the installer. | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-kani-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-kani- | |
| ${{ runner.os }}-cargo- | |
| - name: Install Kani | |
| # No `|| echo` swallowing: a failed install/setup MUST fail the job. Silently | |
| # skipping verification gives false green-CI assurance. --force makes the | |
| # install idempotent on the runner's persistent cargo cache. | |
| run: | | |
| cargo install --locked --force kani-verifier | |
| cargo kani setup | |
| - name: Run Kani verification | |
| # No continue-on-error, no `|| echo`: a failed proof must turn CI red. | |
| run: cargo kani --all-features | |
| cargo-vet: | |
| name: Cargo Vet (Supply Chain) | |
| runs-on: cachekit | |
| if: github.event_name == 'schedule' || github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master | |
| with: | |
| toolchain: "1.85" | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-vet-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-vet- | |
| ${{ runner.os }}-cargo- | |
| - name: Install cargo-vet | |
| run: cargo install cargo-vet --locked | |
| - name: Run cargo vet | |
| run: cargo vet | |
| sbom: | |
| name: Generate SBOM | |
| runs-on: cachekit | |
| if: github.event_name == 'release' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master | |
| with: | |
| toolchain: "1.85" | |
| - name: Cache Rust dependencies | |
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| key: ${{ runner.os }}-cargo-sbom-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-sbom- | |
| ${{ runner.os }}-cargo- | |
| - name: Install cargo-sbom | |
| run: cargo install cargo-sbom --locked | |
| - name: Generate SBOM | |
| run: cargo sbom > cachekit-core-sbom.json | |
| - name: Upload SBOM as release asset | |
| uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| upload_url: ${{ github.event.release.upload_url }} | |
| asset_path: ./cachekit-core-sbom.json | |
| asset_name: cachekit-core-sbom.json | |
| asset_content_type: application/json |