Repository navigation
ci(validation-ledger): green the check on v1.5.2 — cover creds kdbx-e… #293
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: ci | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ['v*'] | |
| pull_request: | |
| workflow_dispatch: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| jobs: | |
| # Format + clippy — single-OS gate, cheapest. | |
| lint: | |
| name: fmt · clippy (deny warnings) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable pinned | |
| with: | |
| components: rustfmt, clippy | |
| - uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2 pinned | |
| - name: format check | |
| run: cargo fmt --all --check | |
| - name: clippy (deny warnings — enforces CLAUDE.md "zero new warnings") | |
| run: cargo clippy --workspace --all-targets -- -D warnings | |
| # Build + test across every platform we ship a release binary for. | |
| # A red build on any of these three blocks merge to main. | |
| test: | |
| name: build · test (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable pinned | |
| - uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2 pinned | |
| - name: install libgssapi (linux) for gssapi feature build | |
| if: runner.os == 'Linux' | |
| run: sudo apt-get update && sudo apt-get install -y libkrb5-dev | |
| - name: install libgssapi (macos) for gssapi feature build | |
| if: runner.os == 'macOS' | |
| run: | | |
| brew install krb5 | |
| echo "LIBGSSAPI_PREFIX=$(brew --prefix krb5)" >> "$GITHUB_ENV" | |
| echo "LIBGSSAPI_IMPL=mit" >> "$GITHUB_ENV" | |
| - name: build | |
| run: cargo build --workspace --release | |
| - name: check supported feature variants | |
| shell: bash | |
| run: | | |
| cargo check --workspace --locked --no-default-features | |
| cargo test -p adhammer-collector --locked --no-default-features no_tls_feature_tests | |
| cargo check -p adhammer --locked --no-default-features --features tls-native | |
| cargo check -p adhammer --locked --features mssql,gssapi | |
| cargo test -p adhammer --locked --features mssql rustls_provider_tests | |
| cargo check -p adhammer-collector --locked --features experimental-gkdi | |
| - name: test (unit + fuzz-lite parsers) | |
| run: cargo test --workspace | |
| # MSRV verification — declared in workspace.package.rust-version. If this | |
| # breaks, either the code drifted past the claimed MSRV or the manifest is stale. | |
| msrv: | |
| name: MSRV verify (rust-version from Cargo.toml) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - name: read MSRV | |
| id: msrv | |
| run: | | |
| version=$(grep '^rust-version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "MSRV=$version" | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master pinned | |
| with: | |
| toolchain: ${{ steps.msrv.outputs.version }} | |
| - uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2 pinned | |
| - name: build with pinned MSRV | |
| run: cargo build --workspace --release | |
| # Fuzz is slow — run it separately on a schedule so PR checks stay fast. | |
| # Left on push/pull_request too, but continue-on-error so it never blocks merge. | |
| # 1.4.9 WS-FUZZ-6 — added 6 targets (pac_credential_info, pac_parse_full, | |
| # dpapi_blob, dpapi_masterkey, gpp_cpassword, gptmpl_parse) on top of the | |
| # existing 2 (sddl_parse, hive_parse). Total 8. | |
| fuzz: | |
| name: cargo-fuzz (nightly, short) | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # nightly pinned | |
| - uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2 pinned | |
| - name: install cargo-fuzz | |
| run: cargo install cargo-fuzz --locked | |
| - name: fuzz each target briefly | |
| # pac_parse_full retired 2026-08-31 — see fuzz/Cargo.toml comment. | |
| # Total 9 targets in scope (sddl_parse, hive_parse, pac_credential_info, | |
| # dpapi_blob, dpapi_masterkey, gpp_cpassword, gptmpl_parse, | |
| # ntlmssp_challenge, ntlmssp_type3). | |
| # `|| true` per-target so we always upload every crash artifact, | |
| # even when a later target also crashes. The overall job is | |
| # continue-on-error so this stays truthful. | |
| run: | | |
| for t in sddl_parse hive_parse pac_credential_info \ | |
| dpapi_blob dpapi_masterkey gpp_cpassword gptmpl_parse \ | |
| ntlmssp_challenge ntlmssp_type3 \ | |
| sanitize_terminal scope_hostname dns_wire; do | |
| cargo +nightly fuzz run "$t" -- -max_total_time=60 -rss_limit_mb=4096 || echo "FUZZ-CRASH: $t" | |
| done | |
| - name: upload fuzz crash artifacts (repro inputs) | |
| if: always() | |
| uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 pinned | |
| with: | |
| name: fuzz-crashes | |
| path: fuzz/artifacts/ | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| # 1.4.9 WS-CI-HARDEN — verify every crate can be packaged in isolation. | |
| # This catches release-day regressions (LICENSE-path bugs, missing metadata, | |
| # circular internal deps) two weeks before a tag is cut, not on tag-cut day. | |
| # --allow-dirty because CI runs against uncommitted state on PRs. | |
| # 1.4.9 local-only manifest sanity: runs on every push. Catches manifest | |
| # typos, missing crates in members, and misdeclared feature sets. Does | |
| # NOT verify crates.io-resolvability (that is `package-check` below, | |
| # gated to release cycles). | |
| manifest-sanity: | |
| name: cargo metadata (manifest sanity, local-only) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable pinned | |
| - name: cargo metadata (resolves workspace + all deps) | |
| run: cargo metadata --format-version=1 --locked > /dev/null | |
| # Registry-independent package inventory. `--list` exercises Cargo's | |
| # include/exclude/readme/license packaging rules without pretending that | |
| # unpublished same-version workspace dependencies already exist on crates.io. | |
| package-inventory: | |
| name: cargo package inventory (all 12 crates) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable pinned | |
| - name: cargo package --list per crate | |
| run: | | |
| set -e | |
| for c in adhammer-core adhammer-secrets adhammer-ldap adhammer-graph \ | |
| adhammer-sysvol adhammer-kerberos adhammer-bloodhound \ | |
| adhammer-checks adhammer-collector adhammer-report \ | |
| adhammer-sdk adhammer; do | |
| echo "=== cargo package -p $c --list ===" | |
| cargo package -p "$c" --allow-dirty --no-verify --list > /dev/null | |
| done | |
| # Post-cascade registry resolution. This can only pass after the bottom-up | |
| # crates.io waves have made each same-version internal dependency visible. | |
| # It is manual by design and must not make a GitHub-binary-only tag red. | |
| package-check: | |
| name: crates.io post-cascade package check | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable pinned | |
| - uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2 pinned | |
| - name: cargo package --allow-dirty per crate | |
| run: | | |
| set -e | |
| for c in adhammer-core adhammer-secrets adhammer-ldap adhammer-graph \ | |
| adhammer-sysvol adhammer-kerberos adhammer-bloodhound \ | |
| adhammer-checks adhammer-collector adhammer-report \ | |
| adhammer-sdk adhammer; do | |
| echo "=== cargo package -p $c ===" | |
| cargo package -p "$c" --allow-dirty --no-verify | |
| done | |
| # 1.5.0 WS-CASCADE-REHEARSAL — bottom-up `cargo publish --dry-run` for | |
| # every publishable crate in DAG order. Reveals whether a real crates.io | |
| # cascade would succeed WITHOUT publishing anything. INFORMATIONAL by | |
| # design (continue-on-error: true): on dev branches with | |
| # [patch.crates-io] present, the first crate reaching a patched dep | |
| # fails-closed with a clear "no matching package found for adhammer-* | |
| # 1.x.x" — that IS the correct signal that the cascade cannot proceed | |
| # until patches are stripped and predecessor siblings published. | |
| # | |
| # Contract: | |
| # - exit 0 = green (would ship) | |
| # - exit 2 = expected fail-closed (patches present, patch-strip needed) | |
| # - exit 1 = unknown failure (real cascade blocker) | |
| cascade-rehearsal: | |
| name: crates.io cascade rehearsal (informational) | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable pinned | |
| - uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2 pinned | |
| - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 pinned | |
| with: | |
| python-version: "3.12" | |
| - name: bash scripts/cascade_rehearsal.sh | |
| run: bash scripts/cascade_rehearsal.sh | |
| # 1.4.9 WS-CI-HARDEN — license + source + banned-crate policy from deny.toml. | |
| # Non-blocking on `warn` (duplicate-versions), blocking on `deny` (unknown | |
| # source, unlicensed crate). Ignore list for accepted advisories is in | |
| # deny.toml with rationale. | |
| cargo-deny: | |
| name: cargo deny (licenses · sources · bans) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable pinned | |
| - uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2 pinned | |
| - name: install cargo-deny | |
| run: cargo install cargo-deny --locked | |
| - name: cargo deny check | |
| run: cargo deny check licenses sources bans advisories | |
| # 1.4.9 Phase-1 gate — docs/VALIDATION.md is the source of truth for what the | |
| # release supports. README / CHANGELOG / PLAN / VECTORS derive from it, not | |
| # the other way around. This job fails when a public claim exceeds what the | |
| # ledger records — "we support X" without a matching row is a bug. | |
| validation-ledger: | |
| name: docs/VALIDATION.md is authoritative | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 pinned | |
| - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 pinned | |
| with: | |
| python-version: "3.12" | |
| - name: enforce ledger vs public surfaces | |
| run: python3 scripts/check_validation_ledger.py | |
| - name: enforce MSRV baseline (Cargo.toml vs docs/POLICY_MSRV.md) | |
| run: python3 scripts/check_msrv_baseline.py | |
| - name: validate receipt JSON shape against docs/receipts/schema.json | |
| run: python3 scripts/validate_receipt.py | |
| - name: verify live-receipt safety boundary | |
| run: | | |
| python3 -m unittest scripts/test_scrub_receipt.py | |
| bash -n scripts/live_validation.sh .githooks/pre-commit | |
| bash scripts/test_pre_commit_hook.sh |