feat(cli): add tls bundle profile#587
Conversation
Implements the v0.8.0 TLS contract-pack profile defined by docs/release/v0.8.0-tls-profile-design.md (PR-A, #585). This is PR-B of the v0.8.0 lane: CLI wiring only, no adapter helpers. What landed: - BundleProfile::Tls variant on the clap-derived profile enum, the manifest_name() arm, and the parse_manifest_profile() round-trip so `verify-bundle` reads the same profile string back. - New BundleEntry variants (TlsValidLeaf, TlsValidChain, TlsNegativeChain, TlsEvidenceDoc) feeding the existing bundle generation + receipt pipeline. The receipts/materialization.json and receipts/audit-surface.json shape is shared with scanner-safe and oidc, so existing tooling (`verify-bundle`, `inspect-bundle`) works unchanged. - Four negative chains generated via the existing uselesskey-x509::srp::chain_negative variants: ExpiredLeaf, NotYetValidLeaf, HostnameMismatch { wrong_hostname }, UnknownCa. - evidence/tls-profile.md emitted alongside the cert bundle with the per-fixture rejection-class table from the design doc. - Documented expected hostname and wrong hostname constants (TLS_EXPECTED_HOSTNAME, TLS_WRONG_HOSTNAME) baked into the bundle so downstream callers can assert against fixed strings. Tests: crates/uselesskey-cli/tests/tls_profile.rs covers layout, single-cert vs full-chain PEM block counts, byte-identical determinism across two invocations, and a `verify-bundle` round-trip with drift detection. Out of scope for this PR (tracked separately): - adapter helpers in uselesskey-rustls / uselesskey-tonic - bundle-proof --profile tls runner (PR-C) - committed expected/ artifacts under examples/ - how-to guide under docs/how-to/ Cross-refs: - design: docs/release/v0.8.0-tls-profile-design.md - negative variants: crates/uselesskey-x509/src/srp/chain_negative.rs
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d54e2f54f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| BundleEntry::TlsNegativeChain { variant, .. } => { | ||
| let valid = tls_valid_chain(fx, label); | ||
| let negative = valid.negative(variant.to_chain_negative()); | ||
| Ok(Artifact::Text(negative.leaf_cert_pem().to_string())) |
There was a problem hiding this comment.
Emit the unknown-CA chain, not just its leaf
When this entry is certs/negative-untrusted-root.pem, this writes only negative.leaf_cert_pem(). ChainNegative::UnknownCa changes the generated root identity, but the leaf is still issued by the same intermediate subject/key used by certs/valid-chain.pem, so file-based consumers that pair this leaf with the bundle's only published intermediate/root can build a trusted path and may accept the supposed unknown-CA fixture instead of exercising that rejection class. Emit the negative chain, or at least its alternate intermediate/root, for this variant.
Useful? React with 👍 / 👎.
| Self::OidcValidJwks | Self::OidcNegativeJwks { .. } => Format::Jwks, | ||
| Self::OidcValidToken | Self::OidcNegativeToken { .. } => Format::JsonManifest, | ||
| Self::TlsValidLeaf | Self::TlsValidChain | Self::TlsNegativeChain { .. } => Format::Pem, | ||
| Self::TlsEvidenceDoc => Format::Pem, |
There was a problem hiding this comment.
Do not record Markdown evidence as PEM X.509
TlsEvidenceDoc is generated as evidence/tls-profile.md, but this records its format as pem (and the entry kind is X.509), so the manifest and receipts advertise a Markdown document as certificate material. Consumers that select x509/pem artifacts from the bundle metadata to parse or install TLS certificates will try to parse the evidence text as a certificate. Exclude the evidence doc from certificate artifact metadata or give it a non-certificate kind/format.
Useful? React with 👍 / 👎.
Close the mutation gap exposed by `cargo mutants` on render_tls_evidence_markdown - the function could be replaced with `"xyzzy".into()` and all tests still passed. Add a content assertion that names each of the 6 fixture filenames and each failure class.
Completes the v0.8.0 TLS lane started by the design doc in #585 and the CLI implementation in #587. Extends `cargo xtask bundle-proof` with a new `--profile tls` arm that produces the release-evidence proof artifact for the TLS contract pack, mirroring the OIDC precedent shipped in v0.7.0. What this PR does: - Adds `tls` to `BUNDLE_PROOF_SUPPORTED_PROFILES` and wires the per-profile default-out-dir / json-filename / markdown-filename / markdown-title / claim-boundary / expected-artifacts helpers. - Adds `TLS_CONTRACT_PACK_PROOF_CLAIM_BOUNDARY` (matches the design-doc scope: pack shape, not downstream verifier correctness; no revocation, no mTLS, no production CA custody). - Drives `bundle`, `verify-bundle`, `inspect-bundle`, the CLI tls contract-pack tests, x509 owner tests, and `no-blob` against the generated TLS bundle, and validates the 7 expected artifacts: 4 cert PEMs (valid leaf, valid chain, 4 negative classes) plus the per-fixture rejection evidence markdown. - Adds `tls-contract-pack-proof` to `release_evidence_steps_minor()` after `oidc-contract-pack-proof`. Patch lane stays unchanged: full contract-pack proofs are minor-only by design. - Adds the "TLS contract-pack proof" row to the minor release-summary table next to the scanner-safe and OIDC rows. Tests: - `bundle_proof_tls_profile_constant_includes_tls` covers the supported profile list, helper outputs, default out dir, and the 7 expected artifact paths. - `bundle_proof_receipt_enforces_tls_contract_pack_contents` and `bundle_proof_receipt_rejects_incomplete_tls_contract_pack` mirror the OIDC receipt tests against a `tls_bundle_proof_manifest()` helper. - `bundle_proof_markdown_summarizes_tls_contract_checks` covers the rendered markdown. - `release_evidence_minor_step_list_includes_tls_contract_pack_proof` and `release_evidence_patch_step_list_excludes_tls_contract_pack_proof` pin the step-list wiring on both lanes. - Updates the existing minor-lane dry-run / summary tests to also assert the new TLS step and artifacts. Validation: - `cargo xtask bundle-proof --profile tls --out target/release-evidence/tls` succeeds end-to-end. Generated bundle has the 6 PEMs + evidence/tls-profile.md + 2 receipts; `tls-contract-pack-proof.{json,md}` are written with all 7 contract-pack checks present. - `cargo xtask bundle-proof --profile scanner-safe` and `--profile oidc` still succeed (regression-clean). - `cargo xtask release-evidence --version 0.8.0-dev --dry-run --summary` plans 16 commands including the new tls step; the `--patch --dry-run` variant plans 10 commands with tls excluded. - `cargo fmt --check -p xtask` and `cargo clippy -p xtask --all-targets -- -D warnings` clean. Out of scope: - `crates/uselesskey-cli/` — the TLS profile already exists post-#587; this PR is xtask-only. - Task-first TLS how-to docs (planned for PR-D). - rustls/tonic adapter helpers (future release). Cross-refs: - Design: `docs/release/v0.8.0-tls-profile-design.md` (PR #585). - CLI impl: PR #587. - OIDC proof precedent (template followed here): `oidc-contract-pack-proof.{json,md}` and the `OIDC_CONTRACT_PACK_PROOF_CLAIM_BOUNDARY` constant.
Task-first user-facing how-to for the `uselesskey bundle --profile tls` contract pack. Walks readers through generating the bundle, the six certificate fixtures (valid leaf, valid chain, four negatives), the expected verifier outcomes per fixture, the documented hostname constants, and the release-grade evidence command. Mirrors the OIDC how-to pattern: terse, task-first prose, explicit "what this proves / does not prove" claim boundaries, and a scanner-safety note that points back to publish-recovery and the design doc for out-of-scope coverage. Completes the v0.8.0 TLS contract-pack lane: design (#585) + CLI profile (#587) + xtask bundle-proof (#588) + this user-facing piece. Pure docs PR; no source changes.
Why
Implements the v0.8.0 TLS contract-pack profile defined in
docs/release/v0.8.0-tls-profile-design.md(PR-A, merged as #585).This is PR-B of the v0.8.0 lane:
uselesskey bundle --profile tls.xtask bundle-proof --profile tlsand releaseevidence routing.
Per the design analysis, this PR is wiring: the negative chain variants
already exist in
uselesskey-x509::srp::chain_negative. The CLI justadds a new
BundleProfilearm and orchestration.What
BundleProfile::Tlson the clap-derived profile enum, with thematching
manifest_name()andparse_manifest_profile()arms soverify-bundleround-trips the profile.BundleEntryvariants (TlsValidLeaf,TlsValidChain,TlsNegativeChain,TlsEvidenceDoc) feeding the existingbundle-generation + receipt pipeline. The
receipts/materialization.jsonandreceipts/audit-surface.jsonshape is shared with
scanner-safeandoidc, so existing tooling(
verify-bundle,inspect-bundle) keeps working unchanged.uselesskey-x509::srp::chain_negativevariants:ExpiredLeaf,NotYetValidLeaf,HostnameMismatch { wrong_hostname },UnknownCa.evidence/tls-profile.mdemitted alongside the cert bundle with theper-fixture rejection-class table from the design doc.
TLS_EXPECTED_HOSTNAMEandTLS_WRONG_HOSTNAMEbaked intothe bundle so downstream callers can assert against fixed strings.
CHANGELOG.md[Unreleased]entry documenting the new profile.Bundle layout produced
Files changed
crates/uselesskey-cli/src/main.rs—BundleProfile::Tls, newBundleEntryarms, TLS-specific chain generator, evidence markdownrenderer.
crates/uselesskey-cli/tests/tls_profile.rs(new) — integrationtests.
CHANGELOG.md—[Unreleased]Added bullet.Validation
Ran clean locally:
cargo build -p uselesskey-cli --all-features --lockedcargo test -p uselesskey-cli --all-features(24 + 4 + 4 passed,including the 4 new
tls_profiletests)cargo xtask fmtcargo clippy --workspace --all-targets --all-features --locked -- -D warnings(post-policy: activate Clippy 1.94/1.95 ratchets #505 Clippy ratchets active, clean)
cargo xtask scanner-safe-reference --check(unchanged; this PRdoes not touch scanner-safe outputs)
cargo xtask cratesio-smoke --path . --skip-install-clicargo xtask docs-sync --checkcargo xtask typoscargo xtask no-blobcargo xtask check-file-policycargo xtask publish-checkgit diff --checkOut of scope
expected/bundle artifacts underexamples/— the TLSbundle is meant to be regenerated under
target/, mirroring the OIDCpack. Any committed proof artifact lands with PR-C.
docs/how-to/guide — separate doc PR once the adapter surface isavailable.
uselesskey-rustlsanduselesskey-tonicadapter helpers — separatePR/release per the design's adapter contract section.
xtask bundle-proof --profile tls— PR-C.Test plan
cargo test -p uselesskey-cli --all-features --test tls_profilepasses.uselesskey bundle --profile tls --out target/fooproduces thesix certificate fixtures, the evidence doc, both receipts, and
manifest.json.uselesskey verify-bundle --path target/fooreturnsstatus: "ok"for the generated bundle.verify-bundlefail withcontent mismatch.(determinism invariant).