feat: decompose core into focused microcrates and add new core infrastructure#21
Conversation
- Introduced `uselesskey-core-keypair` crate for reusable key material helpers. - Implemented `Pkcs8SpkiKeyMaterial` struct with methods for accessing and manipulating PKCS#8 and SPKI key formats. - Added tests for key material accessors and corruption methods. feat: add deterministic key-id helpers - Created `uselesskey-core-kid` crate for generating deterministic key IDs. - Implemented `kid_from_bytes` function for deriving key IDs from byte arrays. - Added tests to ensure deterministic behavior of key ID generation. feat: introduce negative fixture builders - Added `uselesskey-core-negative` crate for generating corrupted PEM/DER data. - Implemented various corruption strategies for PEM and DER formats. - Added tests for corruption functions to ensure stability and uniqueness. feat: implement tempfile-backed artifact sinks - Created `uselesskey-core-sink` crate for managing temporary file artifacts. - Implemented `TempArtifact` struct for creating and managing temporary files. - Added tests to verify file creation, reading, and cleanup behavior. feat: define canonical feature and matrix definitions - Introduced `uselesskey-feature-grid` crate for feature flag and matrix definitions. - Defined core and BDD feature matrices for automation consistency. - Added tests to ensure uniqueness and correctness of feature definitions. feat: add compatibility façade for feature grid - Created `uselesskey-test-grid` crate as a compatibility layer for `uselesskey-feature-grid`. - Re-exported feature matrices and constants for backwards compatibility.
Add default-features = false on microcrate dependency edges and forward std feature to uselesskey-core-id and uselesskey-core-negative. Remove invalid module-level #![no_std] attributes from id.rs and negative/mod.rs (only valid at crate root).
Add content assertion to truncation test to catch argument-swap mutations. All 25 mutants now caught or unviable.
… into existing crates
…keys and OAuth tokens
- Implement `ChainSpec` for generating a three-level X.509 certificate chain (root CA, intermediate CA, leaf). - Introduce methods for setting various properties of the chain specification, including Common Names, RSA key size, and validity periods. - Add `X509Negative` and `ChainNegative` enums for representing invalid X.509 certificates and chains for negative testing. - Create `X509Spec` for defining X.509 certificate specifications, including key usage flags and subject alternative names. - Implement stable byte representations for deterministic derivation and cache keys. - Add comprehensive tests for all new functionalities, ensuring correctness and stability of the specifications.
…rate into workspace
- Moved `KeyUsage`, `NotBeforeOffset`, and `X509Spec` definitions to a new crate `uselesskey-core-x509-spec`. - Added `ChainSpec` struct for generating a three-level X.509 certificate chain. - Updated `uselesskey-core-x509` to re-export types from the new spec crate. - Removed the old `spec.rs` file from `uselesskey-core-x509`. - Introduced stable byte encoding methods for deterministic derivation in both crates. - Updated documentation and tests to reflect the new structure and functionality.
- Introduced `uselesskey-core-jwk` crate with core typed JWK/JWKS models. - Implemented `JwksBuilder` for composing JWKS documents with stable key ordering. - Added support for various JWK types including RSA, EC, OKP, and Oct. - Updated fuzzing targets to include JWK builder tests. - Enhanced xtask to publish the new crate and updated dependency management. - Added property tests to ensure correct behavior of JWK builder. - Created integration tests to verify compatibility between facade and core types.
|
Warning Rate limit exceeded
⌛ 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. 📒 Files selected for processing (1)
WalkthroughModularizes core functionality: adds multiple core crates (jwk, x509-spec/derive, x509, seed, cache, token), turns jwk/x509 into facades re-exporting core crates, centralizes artifact caching with ArtifactCache, and updates workspace members, features, tests, fuzz targets, and xtask propagation logic. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Summary of ChangesHello @EffortlessSteven, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant architectural refactoring by breaking down the core fixture system into smaller, more specialized microcrates. This change aims to improve modularity, reduce compile times, and enhance the granularity of published components. It also adds new capabilities for deterministic seed management, typed caching, and structured handling of JWK and X.509 artifacts, alongside expanded testing and updated build configurations to support the new structure. Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a65b4f877c
ℹ️ 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".
| "uselesskey-core-x509-derive", | ||
| "uselesskey-core-x509", |
There was a problem hiding this comment.
Include core-x509-spec in publish crate list
PUBLISH_CRATES drives both publish_check() and publish preflight iteration, but the newly introduced uselesskey-core-x509-spec crate is not listed here even though uselesskey-core-x509 depends on it (crates/uselesskey-core-x509/Cargo.toml). In release runs this means we never dry-run/publish the spec crate, which can block publishing downstream X.509 crates that now require that dependency version to exist in the registry.
Useful? React with 👍 / 👎.
| "uselesskey-core-x509-derive" => &["uselesskey-core-x509"], | ||
| "uselesskey-core-x509" => &["uselesskey-x509"], |
There was a problem hiding this comment.
Propagate core-x509-spec changes to dependent crates
The PR impact graph adds entries for uselesskey-core-x509-derive and uselesskey-core-x509 but none for uselesskey-core-x509-spec, so a change under crates/uselesskey-core-x509-spec/** will not fan out to uselesskey-core-x509/uselesskey-x509 test targets in cargo xtask pr. That under-tests changes to shared spec/stable-byte logic and can let downstream X.509 regressions slip through CI.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Code Review
This pull request significantly improves the modularity of the uselesskey workspace by decomposing the monolithic core into focused microcrates. The introduction of uselesskey-core-seed, uselesskey-core-cache, and domain-specific microcrates for JWK, tokens, and X.509 derivation enhances compile times and publish granularity. The refactoring of the cache system into a standalone crate with std/no_std support is a great architectural move. I have provided feedback regarding the efficiency and statistical bias of the random token generation helper, which can be improved to better align with the project's goal of providing realistic fixtures.
| pub fn random_base62(rng: &mut impl RngCore, len: usize) -> String { | ||
| let mut out = String::with_capacity(len); | ||
| let mut byte = [0u8; 1]; | ||
|
|
||
| while out.len() < len { | ||
| rng.fill_bytes(&mut byte); | ||
| let idx = byte[0] as usize % BASE62.len(); | ||
| out.push(BASE62[idx] as char); | ||
| } | ||
|
|
||
| out | ||
| } |
There was a problem hiding this comment.
The current implementation of random_base62 is inefficient and introduces a slight statistical bias. Calling rng.fill_bytes for a single byte in a loop is expensive due to the overhead of the RNG call. Additionally, using byte[0] % 62 is biased because 256 is not a multiple of 62, making the first 8 characters of the alphabet (A-H) approximately 25% more likely to appear than the others (5/256 vs 4/256). For more realistic and efficient token generation, consider filling a larger buffer and using a rejection sampling approach or at least reducing the number of RNG calls.
| pub fn random_base62(rng: &mut impl RngCore, len: usize) -> String { | |
| let mut out = String::with_capacity(len); | |
| let mut byte = [0u8; 1]; | |
| while out.len() < len { | |
| rng.fill_bytes(&mut byte); | |
| let idx = byte[0] as usize % BASE62.len(); | |
| out.push(BASE62[idx] as char); | |
| } | |
| out | |
| } | |
| pub fn random_base62(rng: &mut impl RngCore, len: usize) -> String { | |
| let mut out = String::with_capacity(len); | |
| let mut buffer = [0u8; 64]; | |
| while out.len() < len { | |
| let to_fill = (len - out.len()).min(buffer.len()); | |
| rng.fill_bytes(&mut buffer[..to_fill]); | |
| for &b in &buffer[..to_fill] { | |
| let idx = b as usize % BASE62.len(); | |
| out.push(BASE62[idx] as char); | |
| } | |
| } | |
| out | |
| } |
There was a problem hiding this comment.
Pull request overview
This PR performs a major workspace refactor by extracting seed parsing, caching, token/JWK helpers, and X.509 policy/derivation/spec types into focused “core-*” microcrates, while updating facades, adapters, and repo automation (xtask, fuzz, BDD) to match the new dependency graph.
Changes:
- Added new microcrates for core seed parsing/redaction, typed caching, token/JWK helpers, and split X.509 policy/spec/derive.
- Updated existing crates to delegate/re-export to the new core microcrates (e.g.,
uselesskey-jwk,uselesskey-token,uselesskey-x509,uselesskey-core). - Extended CI/automation: updated xtask dependency expansion + publish ordering, added fuzz targets and BDD steps for core-seed.
Reviewed changes
Copilot reviewed 62 out of 64 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| xtask/src/plan.rs | Updates PR-scoped impacted-crate expansion map and adds tests for new crate relationships. |
| xtask/src/main.rs | Updates publish ordering and CI tooling behavior (mutants/fuzz) to account for new crates. |
| fuzz/fuzz_targets/seed_parse.rs | Adds a fuzz target for seed parsing + debug redaction invariants. |
| fuzz/fuzz_targets/jwk_builder.rs | Adds a fuzz target exercising JWKS builder sorting/serialization. |
| fuzz/Cargo.toml | Adds dependencies/binaries for new fuzz targets and adjusts existing fuzz dependencies. |
| crates/uselesskey-x509/src/util.rs | Removes X.509 deterministic time/serial helpers (moved to core microcrates). |
| crates/uselesskey-x509/src/negative.rs | Replaces local negative-policy enums with re-exports from uselesskey-core-x509. |
| crates/uselesskey-x509/src/lib.rs | Re-exports X.509 spec/negative types from uselesskey-core-x509. |
| crates/uselesskey-x509/src/chain_negative.rs | Switches chain-negative policy to uselesskey-core-x509 while keeping fixture helpers. |
| crates/uselesskey-x509/src/chain.rs | Uses deterministic_base_time_from_parts / serial helpers from uselesskey-core-x509. |
| crates/uselesskey-x509/src/cert.rs | Uses uselesskey-core-x509 spec/policy + derivation helpers instead of local modules. |
| crates/uselesskey-x509/Cargo.toml | Drops direct blake3/rand_core deps and depends on uselesskey-core-x509. |
| crates/uselesskey-token/src/token.rs | Delegates token generation/scheme logic to uselesskey-core-token. |
| crates/uselesskey-token/Cargo.toml | Replaces direct deps with uselesskey-core-token and shifts test-only deps to dev-deps. |
| crates/uselesskey-jwk/tests/reexport_integration.rs | Adds integration test ensuring facade/core JWK type compatibility and builder behavior. |
| crates/uselesskey-jwk/src/lib.rs | Converts uselesskey-jwk into a thin re-export facade over uselesskey-core-jwk. |
| crates/uselesskey-jwk/README.md | Updates documentation to reflect facade/re-export role. |
| crates/uselesskey-jwk/Cargo.toml | Changes dependencies to uselesskey-core-jwk (serde stays in core crate). |
| crates/uselesskey-feature-grid/src/lib.rs | Adds a new feature constant for core-seed. |
| crates/uselesskey-core/src/factory_tests.rs | Removes cache downcast tests from core (moved to core-cache). |
| crates/uselesskey-core/src/factory.rs | Replaces embedded cache impl with uselesskey-core-cache::ArtifactCache. |
| crates/uselesskey-core/README.md | Documents that core cache is now powered by uselesskey-core-cache. |
| crates/uselesskey-core/Cargo.toml | Drops dashmap/spin deps and adds uselesskey-core-cache with std feature wiring. |
| crates/uselesskey-core-x509/src/negative.rs | Introduces shared X.509 negative-policy enums and spec mutations. |
| crates/uselesskey-core-x509/src/lib.rs | Adds core X.509 policy crate that re-exports spec + derive helpers. |
| crates/uselesskey-core-x509/README.md | Documents scope/purpose of core X.509 policy crate. |
| crates/uselesskey-core-x509/Cargo.toml | Defines new core X.509 policy crate packaging. |
| crates/uselesskey-core-x509-spec/src/spec.rs | Introduces X.509 spec models + stable encoding helpers. |
| crates/uselesskey-core-x509-spec/src/lib.rs | Adds core X.509 spec crate module exports. |
| crates/uselesskey-core-x509-spec/src/chain_spec.rs | Updates/specifies chain spec modeling and stable encoding details. |
| crates/uselesskey-core-x509-spec/README.md | Documents scope/purpose of core X.509 spec crate. |
| crates/uselesskey-core-x509-spec/Cargo.toml | Defines new core X.509 spec crate packaging. |
| crates/uselesskey-core-x509-derive/src/lib.rs | Introduces deterministic base-time/serial derivation + length-prefixed hashing helpers. |
| crates/uselesskey-core-x509-derive/README.md | Documents scope/purpose of core X.509 derive crate. |
| crates/uselesskey-core-x509-derive/Cargo.toml | Defines new core X.509 derive crate packaging. |
| crates/uselesskey-core-token/src/lib.rs | Adds deterministic token-shape generation helpers (API key/bearer/OAuth JWT-shape). |
| crates/uselesskey-core-token/README.md | Documents scope/purpose of core token crate. |
| crates/uselesskey-core-token/Cargo.toml | Defines new core token crate packaging. |
| crates/uselesskey-core-seed/tests/seed_prop.rs | Adds property tests for seed parsing behavior and robustness. |
| crates/uselesskey-core-seed/src/lib.rs | Adds seed parsing/redaction primitive with hex parsing + blake3 hashing. |
| crates/uselesskey-core-seed/README.md | Documents scope/purpose of core seed crate. |
| crates/uselesskey-core-seed/Cargo.toml | Defines new core seed crate packaging + std feature wiring. |
| crates/uselesskey-core-negative/src/lib.rs | Expands deterministic corruption tests and adds variant-discovery helpers. |
| crates/uselesskey-core-jwk/tests/jwk_prop.rs | Adds property tests for JWKS builder ordering stability. |
| crates/uselesskey-core-jwk/src/lib.rs | Adds core typed JWK/JWKS models and stable-ordering builder implementation. |
| crates/uselesskey-core-jwk/README.md | Documents scope/purpose of core JWK crate. |
| crates/uselesskey-core-jwk/Cargo.toml | Defines new core JWK crate packaging. |
| crates/uselesskey-core-id/tests/seed_integration.rs | Adds integration test to confirm core-id seed derivation works with core-seed type. |
| crates/uselesskey-core-id/src/lib.rs | Re-exports Seed from uselesskey-core-seed and updates derivation accordingly. |
| crates/uselesskey-core-id/README.md | Updates docs to reflect seed re-export rather than seed parsing living here. |
| crates/uselesskey-core-id/Cargo.toml | Adds dependency/feature wiring for uselesskey-core-seed. |
| crates/uselesskey-core-cache/src/lib.rs | Introduces typed cache keyed by ArtifactId with std/no_std backends. |
| crates/uselesskey-core-cache/README.md | Documents scope/purpose of core cache crate. |
| crates/uselesskey-core-cache/Cargo.toml | Defines new core cache crate packaging + std feature wiring. |
| crates/uselesskey-bdd/features/core-seed.feature | Adds BDD scenarios covering seed parsing error handling and debug redaction. |
| crates/uselesskey-bdd/Cargo.toml | Extends BDD feature sets to include core-seed steps. |
| crates/uselesskey-bdd-steps/src/steps/core_seed_steps.rs | Implements cucumber steps for core-seed feature. |
| crates/uselesskey-bdd-steps/src/lib.rs | Wires core-seed steps and world state fields behind the new feature flag. |
| crates/uselesskey-bdd-steps/Cargo.toml | Adds optional dependency/feature flag for uselesskey-core-seed. |
| crates/uselesskey-aws-lc-rs/tests/aws_lc_rs_comprehensive.rs | Adds conversion tests ensuring adapter conversions match fixture key material. |
| Cargo.toml | Adds new microcrates to workspace members and workspace dependency table. |
| Cargo.lock | Updates lockfile for new crates and revised dependency graph. |
| CLAUDE.md | Updates repository crate map documentation for new microcrates/facades. |
| AGENTS.md | Updates repository crate map documentation for new microcrates/facades. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| match crate_name { | ||
| "uselesskey-core-id" => &["uselesskey-core"], | ||
| "uselesskey-core-seed" => &["uselesskey-core-id"], | ||
| "uselesskey-core-id" => &["uselesskey-core-cache", "uselesskey-core"], | ||
| "uselesskey-core-kid" => &["uselesskey-core-keypair", "uselesskey-hmac"], | ||
| "uselesskey-core-keypair" => &["uselesskey-rsa", "uselesskey-ecdsa", "uselesskey-ed25519"], | ||
| "uselesskey-core-cache" => &["uselesskey-core"], | ||
| "uselesskey-core-negative" => &["uselesskey-core"], | ||
| "uselesskey-core-sink" => &["uselesskey-core"], | ||
| "uselesskey-core-token" => &["uselesskey-token"], | ||
| "uselesskey-core-jwk" => &["uselesskey-jwk"], | ||
| "uselesskey-core-x509-derive" => &["uselesskey-core-x509"], | ||
| "uselesskey-core-x509" => &["uselesskey-x509"], | ||
| "uselesskey-core" => &[ |
There was a problem hiding this comment.
dependents() is missing an entry for uselesskey-core-x509-spec. Since uselesskey-core-x509 depends on it, changes to the spec crate won't expand to rebuild uselesskey-core-x509, uselesskey-x509, and downstream adapter crates in PR-scoped CI. Add a mapping like "uselesskey-core-x509-spec" => &["uselesskey-core-x509"] (and consider adding a corresponding unit test).
| const PUBLISH_CRATES: &[&str] = &[ | ||
| "uselesskey-core-seed", | ||
| "uselesskey-core-id", | ||
| "uselesskey-core-cache", | ||
| "uselesskey-core-kid", | ||
| "uselesskey-core-negative", | ||
| "uselesskey-core-sink", | ||
| "uselesskey-core-token", | ||
| "uselesskey-core-jwk", | ||
| "uselesskey-core-x509-derive", | ||
| "uselesskey-core-x509", | ||
| "uselesskey-core", |
There was a problem hiding this comment.
PUBLISH_CRATES is missing uselesskey-core-x509-spec, even though it's a workspace member and is depended on (via uselesskey-core-x509). This will cause cargo xtask publish-check / publish-preflight to skip validating/packaging that crate and can break publish ordering. Add uselesskey-core-x509-spec to the list (before uselesskey-core-x509).
| uselesskey = { path = "../crates/uselesskey", features = ["ecdsa", "token"] } | ||
| rsa = { version = "0.9", features = ["pem", "pkcs8"] } | ||
| uselesskey-jwk = { path = "../crates/uselesskey-jwk" } | ||
| rsa = { version = "0.9", features = ["pem"] } | ||
| p256 = { version = "0.13", features = ["ecdsa", "pkcs8", "pem"] } |
There was a problem hiding this comment.
The rsa dependency no longer enables the pkcs8 feature, but fuzz_targets/rsa_pkcs8_pem_parse.rs imports rsa::pkcs8::DecodePrivateKey and calls RsaPrivateKey::from_pkcs8_pem, which is gated on that feature. Re-enable rsa's pkcs8 feature (in addition to pem) so the fuzz target builds.
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
xtask/src/plan.rs (1)
149-222:⚠️ Potential issue | 🔴 CriticalAdd missing
uselesskey-core-x509-specentry to the dependency propagation graph.
uselesskey-core-x509-specis a workspace crate depended upon byuselesskey-core-x509, but it has no entry in thedependents()function. Changes to it will not trigger downstream rebuilds.Fix
"uselesskey-core-jwk" => &["uselesskey-jwk"], + "uselesskey-core-x509-spec" => &["uselesskey-core-x509"], "uselesskey-core-x509-derive" => &["uselesskey-core-x509"],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@xtask/src/plan.rs` around lines 149 - 222, The dependents() graph is missing an entry for the workspace crate "uselesskey-core-x509-spec", so changes there won't propagate to dependents; update the match in the dependents() function to include a new arm for "uselesskey-core-x509-spec" that lists its downstream crates (at minimum "uselesskey-core-x509" and any other crates that directly depend on the spec), ensuring the propagation includes this crate alongside the existing "uselesskey-core-x509" entry.crates/uselesskey-core-negative/src/lib.rs (4)
40-43: 🧹 Nitpick | 🔵 TrivialShadowing
bytes: &[u8; 32]withbytes: usizein the same scopeThe
_arm binds a newbytes: usizethat shadows the outerbytes: &[u8; 32]digest slice. Renaming the inner binding (e.g.,truncate_chars) removes the ambiguity and aligns with the suggested rename above.♻️ Proposed refactor
_ => { - let bytes = derived_truncate_len(pem, bytes); - corrupt_pem(pem, CorruptPem::Truncate { bytes }) + let chars = derived_truncate_len(pem, bytes); + corrupt_pem(pem, CorruptPem::Truncate { chars }) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/uselesskey-core-negative/src/lib.rs` around lines 40 - 43, The match arm currently shadows the outer `bytes: &[u8; 32]` by binding a new `bytes: usize`; update the binding name in the `_` arm (for example `truncate_chars` or `truncate_len`) and use that new name in the call to `derived_truncate_len(pem, ...)` and when constructing `CorruptPem::Truncate { bytes: ... }` so the slice `bytes` and the usize length are not ambiguous; specifically edit the `_` arm around the calls to `derived_truncate_len` and `corrupt_pem` to use the new identifier instead of `bytes`.
17-17:⚠️ Potential issue | 🟡 Minor
CorruptPem::Truncate { bytes }field semantics mismatch: char count vs. byte count
corrupt_pemconsumes this value as a Unicode character count (pem.chars().take(bytes)), not a byte offset. PEM is always ASCII in practice so chars == bytes, but the public field namebytesdocuments the wrong unit and will mislead callers who pass an actual byte count for non-ASCII input.🔧 Proposed rename
- Truncate { bytes: usize }, + Truncate { chars: usize },- CorruptPem::Truncate { bytes } => pem.chars().take(bytes).collect(), + CorruptPem::Truncate { chars } => pem.chars().take(chars).collect(),And in
corrupt_pem_deterministic:- let bytes = derived_truncate_len(pem, bytes); - corrupt_pem(pem, CorruptPem::Truncate { bytes }) + let chars = derived_truncate_len(pem, bytes); + corrupt_pem(pem, CorruptPem::Truncate { chars })Also applies to: 26-26
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/uselesskey-core-negative/src/lib.rs` at line 17, The Truncate variant's field is misnamed: CorruptPem::Truncate { bytes: usize } is interpreted as a character count by corrupt_pem and corrupt_pem_deterministic (they use pem.chars().take(bytes)), so rename the field to "chars" (CorruptPem::Truncate { chars: usize }) and update all uses in corrupt_pem and corrupt_pem_deterministic to read the new field name; alternatively, if you intended a byte count, change the logic to slice the PEM by bytes using pem.as_bytes().take(bytes) and convert back safely—pick one approach and make the variant, pattern matches, and consumers consistent.
166-172:⚠️ Potential issue | 🟡 Minor
derived_truncate_len_bytescan produce 0 (including forlen == 2) and silently yields empty DERTwo distinct problems:
Zero output: The result range is
[0, len-2]—digest[2] % spancan be0, socorrupt_der_deterministicarms 0 and 2 can return an emptyVec<u8>. The companionderived_truncate_len(line 54) avoids this by adding1:1 + (u16 % span)→ range[1, len-1]. DER corruption should likely guarantee at least one byte remains.Entropy collapse at
len == 2: Whenlen == 2,span == 1→digest[2] % 1 == 0unconditionally, making the truncation length always0regardless of the variant digest. Any 2-byte input is always completely erased.🔧 Proposed fix — mirror the PEM formula
fn derived_truncate_len_bytes(len: usize, digest: &[u8; 32]) -> usize { if len <= 1 { return 0; } let span = len - 1; - digest[2] as usize % span + 1 + (digest[2] as usize % span) // guarantees [1, len-1], never empty }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/uselesskey-core-negative/src/lib.rs` around lines 166 - 172, The truncation can return 0 and collapse for len==2; update derived_truncate_len_bytes to mirror the PEM/derived_truncate_len behavior by computing a value in [1, len-1] instead of [0, len-2]: keep the early return for len <= 1, compute span = len - 1, and return 1 + (digest[2] as usize % span) so at least one byte remains and len==2 yields 1; adjust any comments if present in derived_truncate_len_bytes to reflect the new range.
174-183: 🧹 Nitpick | 🔵 TrivialMissing
proptestcoverage for helper-function invariants (per coding guidelines)The deterministic helpers (
flip_byte,truncate_der,derived_offset,derived_truncate_len_bytes) all have clear, universal algebraic invariants that are only tested with hard-coded inputs. Property-based tests would cover arbitrary byte slices and digests.Example invariants suitable for proptest:
flip_byte: for anyder,offset < der.len()→ exactly one byte differs and its value isinput[offset] ^ 0x01.truncate_der: for anylen < der.len()→output == &der[..len].derived_offset: for anylen > 0,selector→result < len.derived_truncate_len_bytes: for anylen > 1→result < len.As per coding guidelines, "Use
proptestfor property-based tests."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/uselesskey-core-negative/src/lib.rs` around lines 174 - 183, Add property-based tests using proptest to cover the deterministic helpers' invariants: create a #[cfg(test)] proptest module and add proptest cases that generate arbitrary byte slices and lengths to assert the invariants for flip_byte (for any der and offset < der.len() assert exactly one byte differs and new byte == der[offset] ^ 0x01), truncate_der (for any len < der.len() assert output equals &der[..len]), derived_offset (for any len > 0 and arbitrary selector assert result < len), and derived_truncate_len_bytes (for any len > 1 assert result < len); reference the existing helper names flip_byte, truncate_der, derived_offset, derived_truncate_len_bytes and use proptest strategies for Vec<u8> and valid offsets/lengths to reproduce the conditions described in the comment.AGENTS.md (2)
77-90:⚠️ Potential issue | 🟡 MinorWorkspace structure section is missing many newly introduced crates.
The PR adds at least these crates that are absent from the Architecture → Workspace Structure list:
uselesskey-core-seed,uselesskey-core-cache,uselesskey-core-token,uselesskey-core-x509-derive,uselesskey-core-keypair,uselesskey-core-kid,uselesskey-core-negative,uselesskey-core-sink,uselesskey-feature-grid,uselesskey-test-grid.Without these entries, the section is materially incomplete for contributors using this file as orientation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` around lines 77 - 90, The Workspace Structure list is missing several newly added crate entries; update the Architecture → Workspace Structure list to include the new crates by adding lines for uselesskey-core-seed, uselesskey-core-cache, uselesskey-core-token, uselesskey-core-x509-derive, uselesskey-core-keypair, uselesskey-core-kid, uselesskey-core-negative, uselesskey-core-sink, uselesskey-feature-grid, and uselesskey-test-grid alongside the existing crate entries (keep the same bullet style and ordering convention used in the list so they are discoverable and consistent with entries like crates/uselesskey-core and crates/uselesskey-x509).
1-1:⚠️ Potential issue | 🟡 MinorFix the document title: this file is
AGENTS.md, notCLAUDE.md.Line 1 reads
# CLAUDE.md, but the file isAGENTS.md. These appear to be two distinct agent-guidance files, soAGENTS.mdshould have its own heading.📝 Proposed fix
-# CLAUDE.md +# AGENTS.md🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` at line 1, The document header is incorrect: change the first-line heading from "# CLAUDE.md" to a correct title for this file (for example "# AGENTS.md" or another accurate heading that reflects the content) so the file AGENTS.md has its own proper top-level heading; update only the heading line and keep the rest of the content unchanged.crates/uselesskey-x509/src/chain.rs (1)
64-87: 🧹 Nitpick | 🔵 Trivial
newduplicateswith_variant— consider delegating.
newis identical towith_variant(…, "good")except for the hard-coded variant string. Delegating removes the duplication.♻️ Proposed refactor
pub(crate) fn new(factory: Factory, label: &str, spec: ChainSpec) -> Self { - let inner = load_chain_inner(&factory, label, &spec, "good"); - Self { - factory, - label: label.to_string(), - spec, - inner, - } + Self::with_variant(factory, label, spec, "good") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/uselesskey-x509/src/chain.rs` around lines 64 - 87, The new() constructor duplicates with_variant(...) by hardcoding the "good" variant; refactor new to delegate to with_variant to remove duplication: replace new's body so it calls Self::with_variant(factory, label, spec, "good") (ensuring ownership/borrowing matches) rather than calling load_chain_inner itself; update any signature/ownership adjustments as needed so Factory, label (&str), and spec (ChainSpec) are passed correctly to with_variant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/uselesskey-bdd/Cargo.toml`:
- Line 19: The uk-all feature currently declares both
"uselesskey-bdd-steps/uk-all" and every individual sub-feature (e.g.,
"uselesskey-bdd-steps/uk-rsa", "uselesskey-bdd-steps/uk-ecdsa",
"uselesskey-bdd-steps/uk-ed25519", etc.), which is redundant; change the uk-all
feature definition to only include "uselesskey-bdd-steps/uk-all" so the umbrella
feature in the uselesskey-bdd-steps crate controls the sub-features (update the
uk-all array to contain only "uselesskey-bdd-steps/uk-all").
In `@crates/uselesskey-core-cache/Cargo.toml`:
- Line 18: The spin dependency in Cargo.toml includes an unused feature
"spin_mutex"; remove that feature so the dependency only enables the used
"mutex" feature. Edit the spin entry in Cargo.toml (the dependency declaration
for spin) to drop "spin_mutex" from the features array, leaving features =
["mutex"] to match the usage of spin::Mutex in lib.rs.
In `@crates/uselesskey-core-jwk/src/lib.rs`:
- Around line 23-28: The Display impl for Jwks currently calls
serde_json::to_string(...).expect(...), which can panic; change it to handle
serialization errors by mapping them into a fmt::Error and returning
Err(fmt::Error) (e.g., use serde_json::to_string(self).map_err(|_|
fmt::Error).and_then(|s| f.write_str(&s).map_err(|_| fmt::Error)) or match on
the Result and return Err(fmt::Error) on failure). Apply the same non-panicking
pattern to the other Display implementations mentioned (the other impl
fmt::Display blocks in this file) so none call expect() and all convert
serde_json errors to fmt::Error instead.
- Around line 23-28: The Display impls for Jwks, AnyJwk, and PrivateJwk
currently serialize full key material; add a clear rustdoc WARNING above each
Display implementation (impl fmt::Display for Jwks, impl fmt::Display for
AnyJwk, impl fmt::Display for PrivateJwk) stating that Display emits
secret/unredacted material and should not be used for logging or diagnostics,
and then provide a safe alternative by adding a public helper that returns a
redacted JSON (e.g., pub fn to_redacted_json(&self) -> String) which reuses the
redaction logic from the existing Debug impls; update the rustdoc to recommend
using Debug or to_redacted_json() for inspection.
In `@crates/uselesskey-core-negative/src/lib.rs`:
- Around line 271-292: The unbounded loops in find_pem_variant and
find_der_variant can hang; add an iteration cap (e.g., for i in 0..1_000) and
after the loop panic with a clear message indicating exhaustion (include target
and function name) so tests fail fast instead of looping forever; update both
functions (find_pem_variant and find_der_variant) to use the capped range and a
panic! if no matching variant is found.
In `@crates/uselesskey-core-seed/src/lib.rs`:
- Around line 28-37: Update the documentation for from_env_value to explicitly
state the 64-char edge case: when the trimmed input (after optional "0x" prefix)
is exactly 64 characters the function will attempt parse_hex_32 and return that
Err on invalid hex rather than falling back to blake3::hash; otherwise any other
string is hashed with blake3::hash. Reference from_env_value and parse_hex_32 in
the doc comment so callers know this behavior.
In `@crates/uselesskey-core-seed/tests/seed_prop.rs`:
- Around line 14-21: The test comment for
seed_from_env_value_non_64_lengths_are_ok is misleading: update the doc comment
to state the real invariant — that after trimming and stripping an optional "0x"
prefix (using trimmed and after_prefix) any input whose effective length
(after_prefix.len()) is not 64 should cause Seed::from_env_value(&s) to succeed;
mention that the strategy plus prop_assume! excludes 64-length effective inputs
and that non-hex characters in a 64-char string are not covered by this test.
In `@crates/uselesskey-core-x509-derive/Cargo.toml`:
- Around line 19-20: The rcgen = "0.13" and time = "0.3" dependency entries
should be removed from the uselesskey-core-x509-derive Cargo.toml and instead
added to the workspace Cargo.toml under [workspace.dependencies] so both crates
reuse the same pinned versions; locate the rcgen and time lines in the
uselesskey-core-x509-derive manifest (and the duplicate entries in
uselesskey-x509) and move them to the workspace dependencies section alongside
existing workspace = true entries like blake3 and rand_core, preserving the
exact version pins (rcgen = "0.13", time = "0.3") to avoid the rcgen 0.14
breaking changes.
In `@crates/uselesskey-core-x509-derive/src/lib.rs`:
- Around line 34-37: The doc comment for deterministic_base_time is off by one
day: with BASE_TIME_WINDOW_DAYS = 365 and using day_offset % 365 the computed
range is 2025-01-01 through 2025-12-31, not 2026-01-01; update the comment to
reflect the actual window or if you intended to include 2026-01-01 change
BASE_TIME_WINDOW_DAYS to 366 (and keep day_offset % BASE_TIME_WINDOW_DAYS) so
the range becomes 2025-01-01 through 2026-01-01; adjust the text near the
deterministic_base_time function accordingly and ensure references to
BASE_TIME_WINDOW_DAYS and the day_offset modulo logic match the chosen intent.
In `@crates/uselesskey-core-x509/src/negative.rs`:
- Around line 45-66: The X509Negative::SelfSignedButClaimsCA variant is
described as conflicting but the builder sets spec.is_ca = true and
spec.key_usage = KeyUsage::ca(), producing a consistent CA; change the fixture
to actually conflict or make naming/description accurate: either (A) modify the
SelfSignedButClaimsCA branch to introduce a conflict (e.g., keep spec.is_ca =
true but remove or clear key usage like not setting KeyUsage::ca(), or set an
explicit conflicting key usage), or (B) rename the variant/description in
description() to reflect that it produces a valid self-signed CA (update
X509Negative::SelfSignedButClaimsCA string) so behavior and description match;
update references accordingly (look for X509Negative::SelfSignedButClaimsCA,
spec.is_ca, KeyUsage::ca(), and the description() match arm).
In `@crates/uselesskey-jwk/Cargo.toml`:
- Around line 17-18: Replace the inline path+version dependency for
uselesskey-core-jwk with a workspace-managed dependency: add an entry for
uselesskey-core-jwk to the workspace's [workspace.dependencies] in the root
Cargo.toml (the same key and version that was used here) and then in the crate's
Cargo.toml (where the current line "uselesskey-core-jwk = { path = ..., version
= ... }" exists) change that dependency to use workspace = true (remove path and
version). This ensures the crate uses the central workspace version and avoids
silent divergence; update the dependency declaration in the crate that
references uselesskey-core-jwk to "uselesskey-core-jwk = { workspace = true }".
In `@crates/uselesskey-x509/Cargo.toml`:
- Around line 25-26: Remove the unused workspace dependencies by deleting the
rsa.workspace and base64.workspace entries from the crate's Cargo.toml;
specifically remove the lines declaring rsa.workspace = true and
base64.workspace = true so the crate no longer redundantly lists rsa (provided
transitively via uselesskey-rsa) and the unused base64 dependency.
In `@crates/uselesskey-x509/src/chain.rs`:
- Around line 311-317: Replace the silent cast of spec.rsa_bits to u32 with an
explicit try_from conversion to avoid silent truncation: call
u32::try_from(spec.rsa_bits).expect("rsa_bits fits u32") and then .to_be_bytes()
when building rsa_bits; update the rsa_bits binding used in
deterministic_base_time_from_parts and add the necessary TryFrom import if
required to resolve the trait.
In `@xtask/src/main.rs`:
- Around line 242-252: The PUBLISH_CRATES array (const PUBLISH_CRATES) is
missing the new crate "uselesskey-core-x509-spec"; update the PUBLISH_CRATES
constant to include "uselesskey-core-x509-spec" in the list so
publish-check/preflight and mutants include the new crate during release checks,
ensuring the string is added alongside the other "uselesskey-core-*" entries.
---
Outside diff comments:
In `@AGENTS.md`:
- Around line 77-90: The Workspace Structure list is missing several newly added
crate entries; update the Architecture → Workspace Structure list to include the
new crates by adding lines for uselesskey-core-seed, uselesskey-core-cache,
uselesskey-core-token, uselesskey-core-x509-derive, uselesskey-core-keypair,
uselesskey-core-kid, uselesskey-core-negative, uselesskey-core-sink,
uselesskey-feature-grid, and uselesskey-test-grid alongside the existing crate
entries (keep the same bullet style and ordering convention used in the list so
they are discoverable and consistent with entries like crates/uselesskey-core
and crates/uselesskey-x509).
- Line 1: The document header is incorrect: change the first-line heading from
"# CLAUDE.md" to a correct title for this file (for example "# AGENTS.md" or
another accurate heading that reflects the content) so the file AGENTS.md has
its own proper top-level heading; update only the heading line and keep the rest
of the content unchanged.
In `@crates/uselesskey-core-negative/src/lib.rs`:
- Around line 40-43: The match arm currently shadows the outer `bytes: &[u8;
32]` by binding a new `bytes: usize`; update the binding name in the `_` arm
(for example `truncate_chars` or `truncate_len`) and use that new name in the
call to `derived_truncate_len(pem, ...)` and when constructing
`CorruptPem::Truncate { bytes: ... }` so the slice `bytes` and the usize length
are not ambiguous; specifically edit the `_` arm around the calls to
`derived_truncate_len` and `corrupt_pem` to use the new identifier instead of
`bytes`.
- Line 17: The Truncate variant's field is misnamed: CorruptPem::Truncate {
bytes: usize } is interpreted as a character count by corrupt_pem and
corrupt_pem_deterministic (they use pem.chars().take(bytes)), so rename the
field to "chars" (CorruptPem::Truncate { chars: usize }) and update all uses in
corrupt_pem and corrupt_pem_deterministic to read the new field name;
alternatively, if you intended a byte count, change the logic to slice the PEM
by bytes using pem.as_bytes().take(bytes) and convert back safely—pick one
approach and make the variant, pattern matches, and consumers consistent.
- Around line 166-172: The truncation can return 0 and collapse for len==2;
update derived_truncate_len_bytes to mirror the PEM/derived_truncate_len
behavior by computing a value in [1, len-1] instead of [0, len-2]: keep the
early return for len <= 1, compute span = len - 1, and return 1 + (digest[2] as
usize % span) so at least one byte remains and len==2 yields 1; adjust any
comments if present in derived_truncate_len_bytes to reflect the new range.
- Around line 174-183: Add property-based tests using proptest to cover the
deterministic helpers' invariants: create a #[cfg(test)] proptest module and add
proptest cases that generate arbitrary byte slices and lengths to assert the
invariants for flip_byte (for any der and offset < der.len() assert exactly one
byte differs and new byte == der[offset] ^ 0x01), truncate_der (for any len <
der.len() assert output equals &der[..len]), derived_offset (for any len > 0 and
arbitrary selector assert result < len), and derived_truncate_len_bytes (for any
len > 1 assert result < len); reference the existing helper names flip_byte,
truncate_der, derived_offset, derived_truncate_len_bytes and use proptest
strategies for Vec<u8> and valid offsets/lengths to reproduce the conditions
described in the comment.
In `@crates/uselesskey-x509/src/chain.rs`:
- Around line 64-87: The new() constructor duplicates with_variant(...) by
hardcoding the "good" variant; refactor new to delegate to with_variant to
remove duplication: replace new's body so it calls Self::with_variant(factory,
label, spec, "good") (ensuring ownership/borrowing matches) rather than calling
load_chain_inner itself; update any signature/ownership adjustments as needed so
Factory, label (&str), and spec (ChainSpec) are passed correctly to
with_variant.
In `@xtask/src/plan.rs`:
- Around line 149-222: The dependents() graph is missing an entry for the
workspace crate "uselesskey-core-x509-spec", so changes there won't propagate to
dependents; update the match in the dependents() function to include a new arm
for "uselesskey-core-x509-spec" that lists its downstream crates (at minimum
"uselesskey-core-x509" and any other crates that directly depend on the spec),
ensuring the propagation includes this crate alongside the existing
"uselesskey-core-x509" entry.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (63)
AGENTS.mdCLAUDE.mdCargo.tomlcrates/uselesskey-aws-lc-rs/tests/aws_lc_rs_comprehensive.rscrates/uselesskey-bdd-steps/Cargo.tomlcrates/uselesskey-bdd-steps/src/lib.rscrates/uselesskey-bdd-steps/src/steps/core_seed_steps.rscrates/uselesskey-bdd/Cargo.tomlcrates/uselesskey-bdd/features/core-seed.featurecrates/uselesskey-core-cache/Cargo.tomlcrates/uselesskey-core-cache/README.mdcrates/uselesskey-core-cache/src/lib.rscrates/uselesskey-core-id/Cargo.tomlcrates/uselesskey-core-id/README.mdcrates/uselesskey-core-id/src/lib.rscrates/uselesskey-core-id/tests/seed_integration.rscrates/uselesskey-core-jwk/Cargo.tomlcrates/uselesskey-core-jwk/README.mdcrates/uselesskey-core-jwk/src/lib.rscrates/uselesskey-core-jwk/tests/jwk_prop.rscrates/uselesskey-core-negative/src/lib.rscrates/uselesskey-core-seed/Cargo.tomlcrates/uselesskey-core-seed/README.mdcrates/uselesskey-core-seed/src/lib.rscrates/uselesskey-core-seed/tests/seed_prop.rscrates/uselesskey-core-token/Cargo.tomlcrates/uselesskey-core-token/README.mdcrates/uselesskey-core-token/src/lib.rscrates/uselesskey-core-x509-derive/Cargo.tomlcrates/uselesskey-core-x509-derive/README.mdcrates/uselesskey-core-x509-derive/src/lib.rscrates/uselesskey-core-x509-spec/Cargo.tomlcrates/uselesskey-core-x509-spec/README.mdcrates/uselesskey-core-x509-spec/src/chain_spec.rscrates/uselesskey-core-x509-spec/src/lib.rscrates/uselesskey-core-x509-spec/src/spec.rscrates/uselesskey-core-x509/Cargo.tomlcrates/uselesskey-core-x509/README.mdcrates/uselesskey-core-x509/src/lib.rscrates/uselesskey-core-x509/src/negative.rscrates/uselesskey-core/Cargo.tomlcrates/uselesskey-core/README.mdcrates/uselesskey-core/src/factory.rscrates/uselesskey-core/src/factory_tests.rscrates/uselesskey-feature-grid/src/lib.rscrates/uselesskey-jwk/Cargo.tomlcrates/uselesskey-jwk/README.mdcrates/uselesskey-jwk/src/lib.rscrates/uselesskey-jwk/tests/reexport_integration.rscrates/uselesskey-token/Cargo.tomlcrates/uselesskey-token/src/token.rscrates/uselesskey-x509/Cargo.tomlcrates/uselesskey-x509/src/cert.rscrates/uselesskey-x509/src/chain.rscrates/uselesskey-x509/src/chain_negative.rscrates/uselesskey-x509/src/lib.rscrates/uselesskey-x509/src/negative.rscrates/uselesskey-x509/src/util.rsfuzz/Cargo.tomlfuzz/fuzz_targets/jwk_builder.rsfuzz/fuzz_targets/seed_parse.rsxtask/src/main.rsxtask/src/plan.rs
💤 Files with no reviewable changes (1)
- crates/uselesskey-x509/src/util.rs
| default = ["uk-default"] | ||
| uk-default = ["uselesskey-bdd-steps/uk-default"] | ||
| uk-all = ["uselesskey-bdd-steps/uk-all", "uselesskey-bdd-steps/uk-rsa", "uselesskey-bdd-steps/uk-ecdsa", "uselesskey-bdd-steps/uk-ed25519", "uselesskey-bdd-steps/uk-hmac", "uselesskey-bdd-steps/uk-pgp", "uselesskey-bdd-steps/uk-x509", "uselesskey-bdd-steps/uk-jwk", "uselesskey-bdd-steps/uk-token", "uselesskey-bdd-steps/uk-jwt", "uselesskey-bdd-steps/uk-core-id", "uselesskey-bdd-steps/uk-core-kid", "uselesskey-bdd-steps/uk-core-keypair", "uselesskey-bdd-steps/uk-core-negative", "uselesskey-bdd-steps/uk-core-sink", "uselesskey-bdd-steps/uk-aws-lc-rs", "uselesskey-bdd-steps/uk-rustls", "uselesskey-bdd-steps/uk-tonic", "uselesskey-bdd-steps/uk-ring", "uselesskey-bdd-steps/uk-rustcrypto"] | ||
| uk-all = ["uselesskey-bdd-steps/uk-all", "uselesskey-bdd-steps/uk-rsa", "uselesskey-bdd-steps/uk-ecdsa", "uselesskey-bdd-steps/uk-ed25519", "uselesskey-bdd-steps/uk-hmac", "uselesskey-bdd-steps/uk-pgp", "uselesskey-bdd-steps/uk-x509", "uselesskey-bdd-steps/uk-jwk", "uselesskey-bdd-steps/uk-token", "uselesskey-bdd-steps/uk-jwt", "uselesskey-bdd-steps/uk-core-id", "uselesskey-bdd-steps/uk-core-seed", "uselesskey-bdd-steps/uk-core-kid", "uselesskey-bdd-steps/uk-core-keypair", "uselesskey-bdd-steps/uk-core-negative", "uselesskey-bdd-steps/uk-core-sink", "uselesskey-bdd-steps/uk-aws-lc-rs", "uselesskey-bdd-steps/uk-rustls", "uselesskey-bdd-steps/uk-tonic", "uselesskey-bdd-steps/uk-ring", "uselesskey-bdd-steps/uk-rustcrypto"] |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
uk-all lists uselesskey-bdd-steps/uk-all plus all individual sub-features explicitly — likely redundant.
If uselesskey-bdd-steps/uk-all already activates every individual feature in that crate (e.g., uk-rsa, uk-core-seed, etc.), the 20 explicit uselesskey-bdd-steps/<feature> entries are redundant and will accumulate maintenance burden each time a new micro-crate is added (requiring edits in both uselesskey-bdd-steps/uk-all and here).
Consider simplifying to just:
uk-all = ["uselesskey-bdd-steps/uk-all"]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-bdd/Cargo.toml` at line 19, The uk-all feature currently
declares both "uselesskey-bdd-steps/uk-all" and every individual sub-feature
(e.g., "uselesskey-bdd-steps/uk-rsa", "uselesskey-bdd-steps/uk-ecdsa",
"uselesskey-bdd-steps/uk-ed25519", etc.), which is redundant; change the uk-all
feature definition to only include "uselesskey-bdd-steps/uk-all" so the umbrella
feature in the uselesskey-bdd-steps crate controls the sub-features (update the
uk-all array to contain only "uselesskey-bdd-steps/uk-all").
|
|
||
| [dependencies] | ||
| dashmap = { workspace = true, optional = true } | ||
| spin = { version = "0.9.8", default-features = false, features = ["mutex", "spin_mutex"] } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n --type=rust 'SpinMutex' crates/uselesskey-core-cache/src/Repository: EffortlessMetrics/uselesskey
Length of output: 54
🏁 Script executed:
#!/bin/bash
rg -n --type=rust 'spin::' crates/uselesskey-core-cache/src/Repository: EffortlessMetrics/uselesskey
Length of output: 131
Remove the unused spin_mutex feature from the spin dependency.
The codebase only uses spin::Mutex (line 15 of lib.rs), which is provided by the mutex feature. The spin_mutex feature is not used anywhere and should be removed. The dependency should be:
spin = { version = "0.9.8", default-features = false, features = ["mutex"] }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-core-cache/Cargo.toml` at line 18, The spin dependency in
Cargo.toml includes an unused feature "spin_mutex"; remove that feature so the
dependency only enables the used "mutex" feature. Edit the spin entry in
Cargo.toml (the dependency declaration for spin) to drop "spin_mutex" from the
features array, leaving features = ["mutex"] to match the usage of spin::Mutex
in lib.rs.
| impl fmt::Display for Jwks { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| let s = serde_json::to_string(self).expect("serialize JWKS"); | ||
| f.write_str(&s) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Avoid expect() panics inside Display implementations.
fmt::Display is commonly hit in error/logging paths; panicking here (Line 25, Line 277, Line 308, Line 346) can take down callers unexpectedly. Prefer mapping serialization errors to fmt::Error.
Proposed diff (non-panicking Display)
impl fmt::Display for Jwks {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let s = serde_json::to_string(self).expect("serialize JWKS");
+ let s = serde_json::to_string(self).map_err(|_| fmt::Error)?;
f.write_str(&s)
}
}
@@
impl fmt::Display for PublicJwk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let s = serde_json::to_string(self).expect("serialize JWK");
+ let s = serde_json::to_string(self).map_err(|_| fmt::Error)?;
f.write_str(&s)
}
}
@@
impl fmt::Display for PrivateJwk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let s = serde_json::to_string(self).expect("serialize JWK");
+ let s = serde_json::to_string(self).map_err(|_| fmt::Error)?;
f.write_str(&s)
}
}
@@
impl fmt::Display for AnyJwk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let s = serde_json::to_string(self).expect("serialize JWK");
+ let s = serde_json::to_string(self).map_err(|_| fmt::Error)?;
f.write_str(&s)
}
}Also applies to: 275-280, 306-311, 344-349
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-core-jwk/src/lib.rs` around lines 23 - 28, The Display impl
for Jwks currently calls serde_json::to_string(...).expect(...), which can
panic; change it to handle serialization errors by mapping them into a
fmt::Error and returning Err(fmt::Error) (e.g., use
serde_json::to_string(self).map_err(|_| fmt::Error).and_then(|s|
f.write_str(&s).map_err(|_| fmt::Error)) or match on the Result and return
Err(fmt::Error) on failure). Apply the same non-panicking pattern to the other
Display implementations mentioned (the other impl fmt::Display blocks in this
file) so none call expect() and all convert serde_json errors to fmt::Error
instead.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the file structure and size
wc -l crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 113
🏁 Script executed:
# Read the flagged lines to see the Display implementations
sed -n '23,28p' crates/uselesskey-core-jwk/src/lib.rs
echo "---"
sed -n '306,311p' crates/uselesskey-core-jwk/src/lib.rs
echo "---"
sed -n '344,349p' crates/uselesskey-core-jwk/src/lib.rs
echo "---"
sed -n '498,508p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 1119
🏁 Script executed:
# Look for Debug trait implementations to see if they redact
rg -n --type=rust 'impl.*Debug' crates/uselesskey-core-jwk/src/lib.rs -A 5Repository: EffortlessMetrics/uselesskey
Length of output: 1447
🏁 Script executed:
# Check what serde_json::to_string includes for these structures
rg -n --type=rust 'derive.*Serialize' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 421
🏁 Script executed:
# Look for all Display implementations in the file
rg -n --type=rust 'impl.*Display' crates/uselesskey-core-jwk/src/lib.rs -A 5Repository: EffortlessMetrics/uselesskey
Length of output: 956
🏁 Script executed:
# Look at the structure definitions for the private key types
sed -n '85,105p' crates/uselesskey-core-jwk/src/lib.rs
echo "---"
sed -n '154,172p' crates/uselesskey-core-jwk/src/lib.rs
echo "---"
sed -n '253,273p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 1287
🏁 Script executed:
# Check for any doc comments or warnings about Display
rg -n --type=rust '/// .*secret|/// .*private|/// .*leak' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 54
🏁 Script executed:
# Look at PrivateJwk enum definition
sed -n '282,305p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 611
🏁 Script executed:
# Check for any serde skip attributes that might hide fields
rg -n --type=rust '#\[serde.*skip' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 54
🏁 Script executed:
# Get full RsaPrivateJwk structure
sed -n '136,155p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 456
🏁 Script executed:
# Get full OctJwk structure
sed -n '228,252p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 591
🏁 Script executed:
# Check if there's any documentation about this behavior
sed -n '1,25p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 754
🏁 Script executed:
# Look at the test more carefully to see what's being tested
sed -n '498,520p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 851
🏁 Script executed:
# Check RsaPrivateJwk for private key fields
sed -n '136,152p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 401
🏁 Script executed:
# Check OkpPrivateJwk for private key fields
sed -n '200,217p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 406
🏁 Script executed:
# Check what this crate is used for - look at Cargo.toml and any README
cat crates/uselesskey-core-jwk/Cargo.toml | head -20Repository: EffortlessMetrics/uselesskey
Length of output: 681
🏁 Script executed:
# Check if there are any integration tests that use these Display impls in real scenarios
rg -n --type=rust 'to_string\(\)|format!\(' crates/uselesskey-core-jwk/ -B 3 -A 3 | head -60Repository: EffortlessMetrics/uselesskey
Length of output: 3844
🏁 Script executed:
# Look at test helper functions to understand context
rg -n --type=rust 'fn sample_oct_private|fn sample_rsa' crates/uselesskey-core-jwk/src/lib.rs -A 5Repository: EffortlessMetrics/uselesskey
Length of output: 758
🏁 Script executed:
# Check if there's any documentation or comments about the intended use of Display
sed -n '306,325p' crates/uselesskey-core-jwk/src/lib.rsRepository: EffortlessMetrics/uselesskey
Length of output: 645
🏁 Script executed:
# Look at the README to understand the library's purpose
cat crates/uselesskey-core-jwk/README.mdRepository: EffortlessMetrics/uselesskey
Length of output: 489
Display trait serializes all private key material in cleartext, while Debug redacts it.
Display for PrivateJwk, AnyJwk, and Jwks (lines 306–311, 344–349, 23–28) serialize all fields via serde_json::to_string(), including private exponents (d, p, q, dp, dq, qi for RSA; d for EC/OKP) and symmetric keys (k for oct). This differs from their redacted Debug implementations and creates a footgun: accidental {} formatting will leak secrets into CI logs or console output. The test display_outputs_json reinforces that this behavior is intentional.
While serializing full keys makes sense for this fixture library, the Display/Debug inconsistency is risky. Add rustdoc warnings to Display impls that they output secret material unredacted, and consider providing a safe Debug path for aggregate types (AnyJwk, Jwks) to discourage callers from reaching for Display when inspecting values.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-core-jwk/src/lib.rs` around lines 23 - 28, The Display
impls for Jwks, AnyJwk, and PrivateJwk currently serialize full key material;
add a clear rustdoc WARNING above each Display implementation (impl fmt::Display
for Jwks, impl fmt::Display for AnyJwk, impl fmt::Display for PrivateJwk)
stating that Display emits secret/unredacted material and should not be used for
logging or diagnostics, and then provide a safe alternative by adding a public
helper that returns a redacted JSON (e.g., pub fn to_redacted_json(&self) ->
String) which reuses the redaction logic from the existing Debug impls; update
the rustdoc to recommend using Debug or to_redacted_json() for inspection.
| fn find_pem_variant(target: u8) -> String { | ||
| for i in 0u64.. { | ||
| let v = format!("v{i}"); | ||
| let digest = hash32(v.as_bytes()); | ||
| if digest.as_bytes()[0] % 5 == target { | ||
| return v; | ||
| } | ||
| } | ||
| unreachable!() | ||
| } | ||
|
|
||
| /// Find a variant string whose `hash32` first byte % 3 == target. | ||
| fn find_der_variant(target: u8) -> String { | ||
| for i in 0u64.. { | ||
| let v = format!("v{i}"); | ||
| let digest = hash32(v.as_bytes()); | ||
| if digest.as_bytes()[0] % 3 == target { | ||
| return v; | ||
| } | ||
| } | ||
| unreachable!() | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Unbounded search loops in test helpers
find_pem_variant and find_der_variant loop over 0u64.. without an iteration cap. Blake3 distributes uniformly so they terminate in a few iterations in practice, but a hard limit (e.g., 1_000) with a panic! on exhaustion avoids infinite-loop surprises in unusual environments.
♻️ Proposed guard
fn find_pem_variant(target: u8) -> String {
- for i in 0u64.. {
+ for i in 0u64..1_000 {
let v = format!("v{i}");
let digest = hash32(v.as_bytes());
if digest.as_bytes()[0] % 5 == target {
return v;
}
}
- unreachable!()
+ panic!("find_pem_variant: no variant found for target {target} within search limit")
}(Identical change for find_der_variant.)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-core-negative/src/lib.rs` around lines 271 - 292, The
unbounded loops in find_pem_variant and find_der_variant can hang; add an
iteration cap (e.g., for i in 0..1_000) and after the loop panic with a clear
message indicating exhaustion (include target and function name) so tests fail
fast instead of looping forever; update both functions (find_pem_variant and
find_der_variant) to use the capped range and a panic! if no matching variant is
found.
| pub fn from_env_value(value: &str) -> Result<Self, String> { | ||
| let v = value.trim(); | ||
| let hex = v.strip_prefix("0x").unwrap_or(v); | ||
|
|
||
| if hex.len() == 64 { | ||
| return parse_hex_32(hex).map(Self); | ||
| } | ||
|
|
||
| Ok(Self(*blake3::hash(v.as_bytes()).as_bytes())) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Document the 64-char non-hex edge case.
A trimmed input of exactly 64 characters that is not valid hex (e.g. contains !, g, etc.) hits the hex.len() == 64 branch and returns Err rather than falling through to the BLAKE3 hash path. The current doc comment says "any other string (hashed with BLAKE3)" which implies a clean fallback for all non-64-char-hex inputs, but this edge case silently fails instead of hashing. Callers who happen to pass a 64-char non-hex string will be surprised.
📝 Suggested doc clarification
/// Accepted formats:
/// - 64-char hex (with optional `0x` prefix)
- /// - any other string (hashed with BLAKE3)
+ /// - any other string (hashed with BLAKE3)
+ ///
+ /// Note: a string whose effective length (after trimming and stripping `0x`) is
+ /// exactly 64 characters but contains non-hex digits returns `Err`; it is NOT
+ /// hashed as a fallback.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-core-seed/src/lib.rs` around lines 28 - 37, Update the
documentation for from_env_value to explicitly state the 64-char edge case: when
the trimmed input (after optional "0x" prefix) is exactly 64 characters the
function will attempt parse_hex_32 and return that Err on invalid hex rather
than falling back to blake3::hash; otherwise any other string is hashed with
blake3::hash. Reference from_env_value and parse_hex_32 in the doc comment so
callers know this behavior.
| X509Negative::SelfSignedButClaimsCA => { | ||
| // Self-signed but claims to be CA with no real chain context. | ||
| spec.is_ca = true; | ||
| spec.key_usage = KeyUsage::ca(); | ||
| } | ||
| } | ||
|
|
||
| spec | ||
| } | ||
|
|
||
| /// Human-readable description of this negative fixture. | ||
| pub fn description(&self) -> &'static str { | ||
| match self { | ||
| X509Negative::Expired => "Certificate with not_after in the past (expired)", | ||
| X509Negative::NotYetValid => { | ||
| "Certificate with not_before in the future (not yet valid)" | ||
| } | ||
| X509Negative::WrongKeyUsage => { | ||
| "Certificate marked as CA but without keyCertSign key usage" | ||
| } | ||
| X509Negative::SelfSignedButClaimsCA => "Self-signed certificate that claims to be a CA", | ||
| } |
There was a problem hiding this comment.
SelfSignedButClaimsCA is described as conflicting but currently produces a valid CA
This branch sets is_ca = true and KeyUsage::ca(), which is internally consistent. If you need an always-invalid fixture, introduce an actual conflict or adjust the description/variant name to match the behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-core-x509/src/negative.rs` around lines 45 - 66, The
X509Negative::SelfSignedButClaimsCA variant is described as conflicting but the
builder sets spec.is_ca = true and spec.key_usage = KeyUsage::ca(), producing a
consistent CA; change the fixture to actually conflict or make
naming/description accurate: either (A) modify the SelfSignedButClaimsCA branch
to introduce a conflict (e.g., keep spec.is_ca = true but remove or clear key
usage like not setting KeyUsage::ca(), or set an explicit conflicting key
usage), or (B) rename the variant/description in description() to reflect that
it produces a valid self-signed CA (update X509Negative::SelfSignedButClaimsCA
string) so behavior and description match; update references accordingly (look
for X509Negative::SelfSignedButClaimsCA, spec.is_ca, KeyUsage::ca(), and the
description() match arm).
| uselesskey-core-jwk = { path = "../uselesskey-core-jwk", version = "0.3.0" } | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding uselesskey-core-jwk to [workspace.dependencies] and using workspace = true here.
All other internal crates (blake3, rand_core, etc.) are managed via workspace inheritance. Keeping uselesskey-core-jwk as an inline path = … version = … entry will diverge silently if the workspace-level version is ever bumped centrally.
♻️ Suggested change
In the root Cargo.toml:
+ uselesskey-core-jwk = { path = "crates/uselesskey-core-jwk", version = "0.3.0" }In this file:
-uselesskey-core-jwk = { path = "../uselesskey-core-jwk", version = "0.3.0" }
+uselesskey-core-jwk.workspace = true🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-jwk/Cargo.toml` around lines 17 - 18, Replace the inline
path+version dependency for uselesskey-core-jwk with a workspace-managed
dependency: add an entry for uselesskey-core-jwk to the workspace's
[workspace.dependencies] in the root Cargo.toml (the same key and version that
was used here) and then in the crate's Cargo.toml (where the current line
"uselesskey-core-jwk = { path = ..., version = ... }" exists) change that
dependency to use workspace = true (remove path and version). This ensures the
crate uses the central workspace version and avoids silent divergence; update
the dependency declaration in the crate that references uselesskey-core-jwk to
"uselesskey-core-jwk = { workspace = true }".
| rsa.workspace = true | ||
| base64.workspace = true |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if rsa or base64 are referenced directly in uselesskey-x509 source
rg -n --type=rust 'rsa::|base64::' crates/uselesskey-x509/src/Repository: EffortlessMetrics/uselesskey
Length of output: 240
🏁 Script executed:
#!/bin/bash
# Check uselesskey-x509 Cargo.toml to confirm rsa/base64 are listed
echo "=== uselesskey-x509 Cargo.toml lines 20-30 ==="
sed -n '20,30p' crates/uselesskey-x509/Cargo.toml
# Check if uselesskey_rsa depends on rsa and/or base64
echo ""
echo "=== uselesskey_rsa/Cargo.toml dependencies ==="
if [ -f crates/uselesskey_rsa/Cargo.toml ]; then
cat crates/uselesskey_rsa/Cargo.toml
else
echo "File not found"
fiRepository: EffortlessMetrics/uselesskey
Length of output: 430
🏁 Script executed:
#!/bin/bash
# Search for RsaFactoryExt and RsaSpec definitions
echo "=== Searching for RsaFactoryExt and RsaSpec ==="
rg -l 'RsaFactoryExt|RsaSpec' crates/ --type=rust
echo ""
echo "=== Check if uselesskey_rsa is a module or re-export ==="
rg -n 'mod uselesskey_rsa|pub use.*uselesskey_rsa' crates/ --type=rust
echo ""
echo "=== List all crates in repo ==="
fd -type d -mindepth 2 -maxdepth 2 'Cargo.toml' crates/ | xargs dirnameRepository: EffortlessMetrics/uselesskey
Length of output: 1525
🏁 Script executed:
#!/bin/bash
# Check full dependencies section of uselesskey-x509
echo "=== uselesskey-x509/Cargo.toml [dependencies] section ==="
sed -n '/^\[dependencies\]/,/^\[/p' crates/uselesskey-x509/Cargo.toml | head -20
echo ""
echo "=== uselesskey-rsa/Cargo.toml dependencies ==="
sed -n '/^\[dependencies\]/,/^\[/p' crates/uselesskey-rsa/Cargo.toml | head -30Repository: EffortlessMetrics/uselesskey
Length of output: 1005
Remove unused workspace dependencies rsa and base64 from uselesskey-x509.
These dependencies are declared but never used directly in the codebase. The rsa crate is available transitively via uselesskey-rsa, and base64 is not referenced at all. Both should be removed from lines 25–26 of the Cargo.toml.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-x509/Cargo.toml` around lines 25 - 26, Remove the unused
workspace dependencies by deleting the rsa.workspace and base64.workspace
entries from the crate's Cargo.toml; specifically remove the lines declaring
rsa.workspace = true and base64.workspace = true so the crate no longer
redundantly lists rsa (provided transitively via uselesskey-rsa) and the unused
base64 dependency.
| let rsa_bits = (spec.rsa_bits as u32).to_be_bytes(); | ||
| let base_time = deterministic_base_time_from_parts(&[ | ||
| label.as_bytes(), | ||
| spec.leaf_cn.as_bytes(), | ||
| spec.root_cn.as_bytes(), | ||
| &rsa_bits, | ||
| ]); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find ChainSpec struct definition with rsa_bits field
ast-grep --pattern 'struct ChainSpec {
$$$
rsa_bits: $_,
$$$
}'Repository: EffortlessMetrics/uselesskey
Length of output: 54
🏁 Script executed:
# Fallback: search for ChainSpec definition and rsa_bits type
rg -n "struct ChainSpec" --type rust -A 20Repository: EffortlessMetrics/uselesskey
Length of output: 2068
🏁 Script executed:
# Also check the exact location in chain.rs
cd crates/uselesskey-x509/src && cat -n chain.rs | sed -n '280,320p'Repository: EffortlessMetrics/uselesskey
Length of output: 1951
Consider using try_from to make the RSA key size conversion explicit and safe from silent truncation.
spec.rsa_bits is usize, and the silent cast to u32 could truncate values > u32::MAX. While RSA key sizes (typically 2048–4096 bits) will never exceed this in practice, using u32::try_from(spec.rsa_bits).expect("rsa_bits fits u32") makes the intent clearer and guards against bad values at the call site.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/uselesskey-x509/src/chain.rs` around lines 311 - 317, Replace the
silent cast of spec.rsa_bits to u32 with an explicit try_from conversion to
avoid silent truncation: call u32::try_from(spec.rsa_bits).expect("rsa_bits fits
u32") and then .to_be_bytes() when building rsa_bits; update the rsa_bits
binding used in deterministic_base_time_from_parts and add the necessary TryFrom
import if required to resolve the trait.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
crates/uselesskey-core-cache/Cargo.toml (1)
18-18: Remove unusedspin_mutexfeature (already flagged previously).#!/bin/bash # Verify whether SpinMutex or related APIs are used anywhere. rg -n --type=rust -C3 '\bSpinMutex\b|\bspin::SpinMutex\b' rg -n --type=rust -C3 '\bspin::Mutex\b'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/uselesskey-core-cache/Cargo.toml` at line 18, The Cargo.toml currently enables an unused feature "spin_mutex" on the spin dependency (spin = { version = "0.9.8", default-features = false, features = ["mutex", "spin_mutex"] }); remove "spin_mutex" from the features array, run the provided verification commands (searching for SpinMutex, spin::SpinMutex, and spin::Mutex) to ensure no code references remain, then run cargo build and cargo test (and cargo update if needed) to regenerate Cargo.lock and confirm the change is safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@crates/uselesskey-core-cache/Cargo.toml`:
- Line 18: The Cargo.toml currently enables an unused feature "spin_mutex" on
the spin dependency (spin = { version = "0.9.8", default-features = false,
features = ["mutex", "spin_mutex"] }); remove "spin_mutex" from the features
array, run the provided verification commands (searching for SpinMutex,
spin::SpinMutex, and spin::Mutex) to ensure no code references remain, then run
cargo build and cargo test (and cargo update if needed) to regenerate Cargo.lock
and confirm the change is safe.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
crates/uselesskey-core-cache/Cargo.tomlcrates/uselesskey-core-id/Cargo.tomlcrates/uselesskey-core-x509/Cargo.tomlcrates/uselesskey-core/Cargo.tomlxtask/src/main.rsxtask/src/plan.rs
Summary
Major architectural refactoring that decomposes the monolithic core fixture system into highly focused microcrates, improving modularity, compile times, and publish granularity. Also adds critical new capabilities for deterministic seed handling, typed caching, and comprehensive adapter tests.
New core infrastructure crates
uselesskey-core-seed— Seed parsing and redaction primitives. Parses hex or free-form string inputs into 32-byte seeds, with secureDebugoutput that redacts seed material to prevent log leakage.uselesskey-core-cache— Identity-keyed typed cache. Process-localDashMap(orspin::Mutex<BTreeMap>underno_std) keyed byArtifactId, stores fixtures asArc<dyn Any + Send + Sync>with explicit panic on type mismatch.New domain microcrates
uselesskey-core-jwk— Typed JWK/JWKS models (PublicJwk,PrivateJwk,AnyJwk) andJwksBuilderwith stablekid-ordered serialization. Shape/serialization only — decoupled from key generation.uselesskey-core-token— Deterministic token-shape generation helpers (API keys, bearer tokens, OAuth access tokens) with realistic formatting, no signing logic.uselesskey-core-x509-spec— X.509 fixture spec models (X509Spec,ChainSpec,KeyUsage,NotBeforeOffset) with stable byte encodings for cache keys.uselesskey-core-x509-derive— Deterministic X.509 derivation: base times from identity, positive serial numbers, length-prefixed hashing to avoid input-boundary collisions.uselesskey-core-x509— Aggregator for X.509 negative-policy types (X509Negative,ChainNegative) plus re-exports from spec/derive crates.Refactoring
uselesskey-coreintouselesskey-core-cache; removed directdashmap/spindeps from core.uselesskey-jwkintouselesskey-core-jwk;uselesskey-jwkis now a thin facade re-exporting core types.uselesskey-core-token;uselesskey-tokendelegates viagenerate_token().uselesskey-x509now depends on unified core types and dropsutil.rs.Testing
seed_prop.rs), JWK serialization properties (jwk_prop.rs).core-seed.featurewith step implementations for hex parsing, error handling, and debug redaction.Build/CI
membersand[workspace.dependencies]expanded for all new crates.PUBLISH_CRATESorder updated in xtask to respect new dependency graph.xtask/src/plan.rsdependency map updated so PR-scoped CI correctly triggers downstream rebuilds (e.g., seed change → core-id → core-cache → core → all).Test plan
cargo check --workspace --all-featurespassescargo xtask testpasses (all workspace tests)cargo xtask clippycleancargo xtask bddpassescargo xtask feature-matrixpassescargo xtask publish-checkpasses