Skip to content

deps(rust): bump the rust-all group with 13 updates#4

Closed
dependabot[bot] wants to merge 345 commits into
mainfrom
dependabot/cargo/rust-all-2b0126d790
Closed

deps(rust): bump the rust-all group with 13 updates#4
dependabot[bot] wants to merge 345 commits into
mainfrom
dependabot/cargo/rust-all-2b0126d790

Conversation

@dependabot
Copy link
Copy Markdown
Contributor

@dependabot dependabot Bot commented on behalf of github Apr 13, 2026

Bumps the rust-all group with 13 updates:

Package From To
thiserror 1.0.69 2.0.18
reqwest 0.11.27 0.12.28
libc 0.2.184 0.2.185
base64 0.21.7 0.22.1
syslog 6.1.1 7.0.0
gethostname 0.4.3 1.1.0
getrandom 0.3.4 0.4.2
sha1 0.10.6 0.11.0
sha2 0.10.9 0.11.0
hmac 0.12.1 0.13.0
directories 5.0.1 6.0.0
dirs 5.0.1 6.0.0
cryptoki 0.7.0 0.12.0

Updates thiserror from 1.0.69 to 2.0.18

Release notes

Sourced from thiserror's releases.

2.0.18

2.0.17

  • Use differently named __private module per patch release (#434)

2.0.16

  • Add to "no-std" crates.io category (#429)

2.0.15

  • Prevent Error::provide API becoming unavailable from a future new compiler lint (#427)

2.0.14

  • Allow build-script cleanup failure with NFSv3 output directory to be non-fatal (#426)

2.0.13

  • Documentation improvements

2.0.12

  • Prevent elidable_lifetime_names pedantic clippy lint in generated impl (#413)

2.0.11

2.0.10

  • Support errors containing a generic type parameter's associated type in a field (#408)

2.0.9

  • Work around missing_inline_in_public_items clippy restriction being triggered in macro-generated code (#404)

2.0.8

  • Improve support for macro-generated derive(Error) call sites (#399)

2.0.7

  • Work around conflict with #[deny(clippy::allow_attributes)] (#397, thanks @​zertosh)

2.0.6

  • Suppress deprecation warning on generated From impls (#396)

2.0.5

  • Prevent deprecation warning on generated impl for deprecated type (#394)

2.0.4

  • Eliminate needless_lifetimes clippy lint in generated From impls (#391, thanks @​matt-phylum)

2.0.3

  • Support the same Path field being repeated in both Debug and Display representation in error message (#383)
  • Improve error message when a format trait used in error message is not implemented by some field (#384)

2.0.2

... (truncated)

Commits
  • dc0f6a2 Release 2.0.18
  • 0275292 Touch up PR 443
  • 3c33bc6 Merge pull request #443 from LucaCappelletti94/master
  • 995939c Reproduce issue 442
  • 21653d1 Made clippy lifetime allows conditional
  • 45e5388 Update actions/upload-artifact@v5 -> v6
  • 386aac1 Update actions/upload-artifact@v4 -> v5
  • ec50561 Update actions/checkout@v5 -> v6
  • 247eab5 Update name of empty_enum clippy lint
  • 91b181f Raise required compiler to Rust 1.68
  • Additional commits viewable in compare view

Updates reqwest from 0.11.27 to 0.12.28

Release notes

Sourced from reqwest's releases.

v0.12.28

What's Changed

Full Changelog: seanmonstar/reqwest@v0.12.27...v0.12.28

v0.12.27

tl;dr

  • Add ClientBuilder::windows_named_pipe(name) option that will force all requests over that Windows Named Pipe.

What's Changed

Full Changelog: seanmonstar/reqwest@v0.12.26...v0.12.27

v0.12.26

tl;dr

  • Fix sending Accept-Encoding header only with values configured with reqwest, regardless of underlying tower-http config.

What's Changed

Full Changelog: seanmonstar/reqwest@v0.12.25...v0.12.26

v0.12.25

Highlights

  • Add Error::is_upgrade() to determine if the error was from an HTTP upgrade.
  • Fix sending Proxy-Authorization if only username is configured.
  • Fix sending Proxy-Authorization to HTTPS proxies when the target is HTTP.
  • Refactor internal decompression handling to use tower-http.

What's Changed

... (truncated)

Changelog

Sourced from reqwest's changelog.

v0.12.28

  • Fix compiling on Windows if TLS and SOCKS features are not enabled.

v0.12.27

  • Add ClientBuilder::windows_named_pipe(name) option that will force all requests over that Windows Named Piper.

v0.12.26

  • Fix sending Accept-Encoding header only with values configured with reqwest, regardless of underlying tower-http config.

v0.12.25

  • Add Error::is_upgrade() to determine if the error was from an HTTP upgrade.
  • Fix sending Proxy-Authorization if only username is configured.
  • Fix sending Proxy-Authorization to HTTPS proxies when the target is HTTP.
  • Refactor internal decompression handling to use tower-http.

v0.12.24

  • Refactor cookie handling to an internal middleware.
  • Refactor internal random generator.
  • Refactor base64 encoding to reduce a copy.
  • Documentation updates.

v0.12.23

  • Add ClientBuilder::unix_socket(path) option that will force all requests over that Unix Domain Socket.
  • Add ClientBuilder::retry(policy) and reqwest::retry::Builder to configure automatic retries.
  • Add ClientBuilder::dns_resolver2() with more ergonomic argument bounds, allowing more resolver implementations.
  • Add http3_* options to blocking::ClientBuilder.
  • Fix default TCP timeout values to enabled and faster.
  • Fix SOCKS proxies to default to port 1080
  • (wasm) Add cache methods to RequestBuilder.

v0.12.22

  • Fix socks proxies when resolving IPv6 destinations.

v0.12.21

  • Fix socks proxy to use socks4a:// instead of socks4h://.
  • Fix Error::is_timeout() to check for hyper and IO timeouts too.
  • Fix request Error to again include URLs when possible.
  • Fix socks connect error to include more context.
  • (wasm) implement Default for Body.

v0.12.20

... (truncated)

Commits

Updates libc from 0.2.184 to 0.2.185

Release notes

Sourced from libc's releases.

0.2.185

Added

  • EspIDF: Add espidf_picolibc cfg for picolibc O_* flag values (#5035)
  • Hexagon: add missing constants and fix types for linux-musl (#5042)
  • Redox: Add semaphore functions (#5051)
  • Windows: Add sprintf, snprintf, and the scanf family (#5024)

Fixed

  • Hexagon: Decouple time64 types from musl symbol redirects (#5040)
  • Horizon: Change POLL constants from c_short to c_int (#5045)
Changelog

Sourced from libc's changelog.

0.2.185 - 2026-04-13

Added

  • EspIDF: Add espidf_picolibc cfg for picolibc O_* flag values (#5035)
  • Hexagon: add missing constants and fix types for linux-musl (#5042)
  • Redox: Add semaphore functions (#5051)
  • Windows: Add sprintf, snprintf, and the scanf family (#5024)

Fixed

  • Hexagon: Decouple time64 types from musl symbol redirects (#5040)
  • Horizon: Change POLL constants from c_short to c_int (#5045)
Commits
  • 71d5bfc libc: Release 0.2.185
  • 1027d1c Revert "ci: Pin nightly to 2026-04-01"
  • 0e9c6e5 redox: Add semaphore functions
  • 24ef457 feat: add back support for gnu windows x86 in ci
  • aa75caf horizon: Change POLL constants from c_short to c_int
  • b7eda5a hexagon: add missing constants and fix types for linux-musl
  • d4613f9 newlib/espidf: Add espidf_picolibc cfg for picolibc O_* flag values
  • c89fd76 Fix typo in Padding comments
  • b3264b2 hexagon: decouple time64 types from musl symbol redirects
  • db1ebee ci: Pin nightly to 2026-04-01
  • Additional commits viewable in compare view

Updates base64 from 0.21.7 to 0.22.1

Changelog

Sourced from base64's changelog.

0.22.1

  • Correct the symbols used for the predefined alphabet::BIN_HEX.

0.22.0

  • DecodeSliceError::OutputSliceTooSmall is now conservative rather than precise. That is, the error will only occur if the decoded output cannot fit, meaning that Engine::decode_slice can now be used with exactly-sized output slices. As part of this, Engine::internal_decode now returns DecodeSliceError instead of DecodeError, but that is not expected to affect any external callers.
  • DecodeError::InvalidLength now refers specifically to the number of valid symbols being invalid (i.e. len % 4 == 1), rather than just the number of input bytes. This avoids confusing scenarios when based on interpretation you could make a case for either InvalidLength or InvalidByte being appropriate.
  • Decoding is somewhat faster (5-10%)
Commits
  • e144006 v0.22.1
  • 64cca59 Merge pull request #271 from JobanSD/patch-1
  • 838355e Correct BinHex 4.0 alphabet according to specifications
  • bf15ccf Merge pull request #270 from marshallpierce/mp/clippy
  • fc6aabe Appease clippy
  • 9a518a2 Merge pull request #267 from bdura/patch-1
  • d96c80f Merge branch 'marshallpierce:master' into patch-1
  • 5d70ba7 Merge pull request #269 from marshallpierce/mp/decode-precisely
  • efb6c00 Release notes
  • 2b91084 Add some tests to boost coverage
  • Additional commits viewable in compare view

Updates syslog from 6.1.1 to 7.0.0

Commits

Updates gethostname from 0.4.3 to 1.1.0

Updates getrandom from 0.3.4 to 0.4.2

Changelog

Sourced from getrandom's changelog.

0.4.2 - 2026-03-03

Changed

  • Bump r-efi dependency to v6 #814

Fixed

  • Read errno only when it is set #810
  • Check the return value of ProcessPrng on Windows #811

#810: rust-random/getrandom#810 #811: rust-random/getrandom#811 #814: rust-random/getrandom#814

0.4.1 - 2026-02-03

Fixed

  • Documentation build on docs.rs #801

#801: rust-random/getrandom#801

0.4.0 - 2026-02-02

Added

  • RawOsError type alias #739
  • SysRng behind new feature sys_rng #751
  • WASIp3 support #779
  • extern_impl opt-in backend #786 #794
  • Motor OS support #797

Changed

  • Use Edition 2024 and MSRV 1.85 #749

#739: rust-random/getrandom#739 #749: rust-random/getrandom#749 #751: rust-random/getrandom#751 #779: rust-random/getrandom#779 #786: rust-random/getrandom#786 #794: rust-random/getrandom#794 #797: rust-random/getrandom#797

Commits

Updates sha1 from 0.10.6 to 0.11.0

Commits

Updates sha2 from 0.10.9 to 0.11.0

Commits

Updates hmac from 0.12.1 to 0.13.0

Commits

Updates directories from 5.0.1 to 6.0.0

Commits

Updates dirs from 5.0.1 to 6.0.0

Commits

Updates cryptoki from 0.7.0 to 0.12.0

Changelog

Sourced from cryptoki's changelog.

cryptoki-0.12.0 (2026-01-22)

Full Changelog

Implemented enhancements:

  • Accelerate fetching attributes from an object handle - cleaned up version #341 (keldonin)

Closed issues:

  • session no longer send #339
  • Unmaintained Dependency paste (RUSTSEC-2024-0436) #279

Merged pull requests:

cryptoki-0.11.0 (2025-12-19)

Full Changelog

cryptoki-sys-0.5.0 (2025-12-19)

Full Changelog

Implemented enhancements:

  • Add a way to pass a pointer for the "reserved" argument in CInitializeArgs() #321
  • Support vendor defined error codes #299

Closed issues:

  • Undefined behavior in CK_ATTRIBUTE::try_from or Session::get_attributes #323
  • Consider opening visibility on ObjectHandle and SessionHandle #316
  • Compatibility with Luna HSM's libCryptoki2_64.so? #315
  • Run tests against kryoptic main #311
  • Consider wrapping C_Decapsulate #309
  • Considering adding support for CKM_CONCATENATE_DATA_AND_BASE #308
  • Run CI on more different architectures in some more systematic manner #285
  • Upgrade Secrecy Crate #284
  • Building on ix86 fails #282
  • EcKdf documentation unclear #281
  • Add a new constructor that does not call C_Finalize when dropped #208
  • finalize() without drop()? #150

... (truncated)

Commits
  • bf51837 Bump cryptoki
  • 7acb6ed Merge pull request #347 from bal-e/remove-paste-dep
  • 0bd773c Merge pull request #325 from EliseChouleur/better-handle-drop-error
  • d80e2c7 Remove 'paste' dependency
  • 1301319 Put closed to true only if close doesn't fail
  • d7fc6c6 Make enum Eq, copy, clone
  • 059a8d9 Remove useless condition and early return
  • 199303f Create an enum for close on drop option for better readability
  • 891d147 Create a variable for the number of threads in thread_local_session example
  • ac3c634 add Session's close state in Cell to be mutable event in a non mut 'self' fun...
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore <dependency name> major version will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
  • @dependabot ignore <dependency name> minor version will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • @dependabot ignore <dependency name> will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
  • @dependabot unignore <dependency name> will remove all of the ignore conditions of the specified dependency
  • @dependabot unignore <dependency name> <ignore condition> will remove the ignore condition of the specified dependency and ignore conditions

prodnull and others added 30 commits March 12, 2026 08:44
- Add unix-oidc-agent/src/askpass.rs: SSH_ASKPASS handler for PAM keyboard-interactive DPoP conversation
- DPOP_NONCE: stores server nonce in PPID-keyed tmpfile (0600 perms)
- DPOP_PROOF: reads stored nonce, calls GetProof IPC, caches token, prints dpop_proof
- OIDC Token: reads cached token tmpfile or falls back to GetProof IPC
- Unrecognized prompts: safe default (empty line), no-fail
- Add SshAskpass subcommand to Commands enum in main.rs (positional <PROMPT> arg)
- 13 unit tests covering: permissions, PPID keying, prompt parsing, read_and_delete, path helpers
…wiring

- fix: append \\n after SessionClosed JSON in notify_agent_session_closed() so
  agent BufReader::read_line() returns immediately (was blocking 2s timeout)
- feat: add PamTimeoutsConfig struct with clock_skew_future_secs=5 and
  clock_skew_staleness_secs=60 defaults; wire as PolicyConfig.timeouts field
- feat: add DPoPAuthConfig::from_policy() to read clock skew from PolicyConfig.timeouts
- fix: authenticate_with_dpop() now sets ValidationConfig.clock_skew_tolerance_secs
  from policy.timeouts.clock_skew_staleness_secs (not hardcoded 60)
- fix: lib.rs pam_sm_authenticate() reads clock skew from PolicyConfig instead of
  hardcoded max_proof_age=60 and clock_skew_future_secs=5
- chore: remove DPoPAuthConfig::from_env() dead code; replace with from_policy()
- chore: remove dead CLOCK_SKEW_TOLERANCE constant from validation.rs
- test: add TDD tests for all behaviors (newline framing, PamTimeoutsConfig
  YAML/default/env-override, from_policy() clock skew)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fix: TOCTOU unwrap() on HashMap::get() at second read lock in
  handle_step_up_result() replaced with let-else returning
  AgentResponse::error("Step-up result already consumed", "STEP_UP_CONSUMED")
- test: add test_handle_step_up_result_no_panic_on_missing_entry verifying
  error response for unknown correlation IDs (not panic)
- test: add test_handle_step_up_result_finished_task_returns_complete verifying
  the happy path (finished task → StepUpComplete) still works after the fix
- resolves clippy::unwrap_used lint violation in socket.rs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three bugs affecting both test_token_exchange.sh and test_dpop_binding.sh on
macOS (and any platform where bash $(cat) captures binary via command substitution):

1. DER signature parser off-by-one (OFFSET=4 → OFFSET=6): the old code read the
   INTEGER tag byte (0x02) as the R length, producing a garbage 2-byte R and a
   misaligned S. Fixed by starting at offset 6 to skip SEQUENCE header (30 XX)
   and the INTEGER tag (02) before reading the length.

2. macOS base64 trailing newline not stripped: macOS base64(1) appends a newline
   after each output line. The tr -d '=' did not remove it, embedding a '\n' inside
   JWK x/y coordinates and causing "Point not on curve" in Keycloak's BouncyCastle
   EC key parser. Fixed by inserting tr -d '\n' before tr '+/' '-_'.

3. Bash variable binary corruption (0x5c 0x30 → 0x00): when binary data is captured
   via $(cat) in a shell variable, bash interprets backslash sequences such as
   0x5c 0x30 (\0) as a null byte, silently truncating or corrupting EC coordinate
   bytes. Fixed by piping binary directly to base64 in all locations where raw binary
   (EC coordinates, SHA-256 digest, ECDSA signature) was previously routed through
   the base64url_encode() function's $(cat) capture pattern.

4. Token exchange without audience: test_token_exchange.sh step 7 was sending
   audience=target-host-b which triggers "Client not allowed to exchange" in
   Keycloak 26 standard token exchange V2. Fixed to try without audience first
   (matching test_token_exchange.py behavior) and fall back to explicit audience.

Verified: all three scripts exit 0 against a live Keycloak 26.2 instance with
docker-compose.token-exchange.yaml (clean volume, fresh realm import).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Running cargo fmt --all finds formatting drift accumulated across previous
phase implementations. No logic changes — purely formatting normalization
required for CI check to pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tss-esapi-sys v0.5.0 requires the tss2-sys system library. Add
libtss2-dev to the check job apt-get install so clippy can compile
all features, unblocking the token-exchange job (needs: [check]).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add missing clock_skew_tolerance_secs field to test ValidationConfig
  initializer in validation.rs (field added in Phase 14 but test helper
  not updated)
- Fix mlock errno access: use __errno_location() on Linux, __error() on
  macOS — libc::__error() is macOS-only but was used unconditionally
- Fix tss-esapi v7.6 API breaks in tpm_signer.rs:
  - EccScheme moved from interface_types::algorithm to structures
  - PersistentTpmHandle moved from structures to handles
  - Provision moved from crate root to interface_types::resource_handles
  - StructureTag path: use constants::StructureTag (not interface_types)
  - Hierarchy::Null replaces tss_esapi::constants::tpm::Handles::Null
  - Public::Ecc { unique, .. } pattern replaces PublicIdUnion::Ecc(unique)
    (PublicIdUnion was removed; Public is now a plain enum with named fields)

These are pre-existing bugs that prevented the CI check job from passing,
which in turn blocked the token-exchange job via needs: [check].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add #[allow(clippy::unwrap_used, clippy::expect_used)] to 16 test
  modules in pam-unix-oidc and unix-oidc-agent test files
- Fix TPM signer compilation on Linux: EccCurveIdentifier type,
  Persistent handle conversion, TPM2_RH_NULL constant, deprecated
  as_slice -> as_ref
- Inline format args (uninlined_format_args lint, ~80 instances)
- Fix misc clippy: assert_eq!(x,true)->assert!(x), writeln!,
  struct initializer syntax, filter_map, redundant import
- Guard macOS-only launchd constants with #[cfg(target_os = "macos")]
- Fix askpass test race condition with unique temp paths per test
- Add SecureStorage trait import for headless_storage Linux compilation

Verified clean on both platforms:
  macOS: cargo clippy --workspace --all-targets -- -D warnings
  Linux: docker rust:1.88 with --all-features (includes TPM/PKCS#11)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ak-glass

Phase 16 implements INT-01 through INT-04 integration test infrastructure:

- INT-01: CIBA-enabled Keycloak test realm (ciba-test-realm.json) with
  poll-mode backchannel auth, DPoP binding, and ACR LoA mapper. New
  docker-compose.ciba-integration.yaml with --features=ciba.

- INT-02: Step-up IPC full-flow integration test (step_up_ipc.rs) using
  wiremock-rs to mock OIDC discovery + CIBA endpoints. Tests StepUp →
  StepUpPending → poll → StepUpComplete, NOT_LOGGED_IN guard, and
  unknown correlation_id rejection.

- INT-03: Break-glass fallback tests — shell script validates IdP-down
  graceful failure and recovery; Rust integration test validates policy
  parsing, account matching, and unreachable-issuer independence.

- INT-04: ACR validation via CIBA integration test against live Keycloak
  with acr-loa-mapper configured. Direct-grant fallback validates ACR
  claim presence.

CI: New ciba-integration job in ci.yml.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- REQUIREMENTS.md: INT-01..INT-04 marked Complete
- ROADMAP.md: Phase 16 status updated, Phase 12 marked superseded
- docs/threat-model.md: STRIDE-based threat model covering all trust
  boundaries (IdP↔Agent, Agent↔PAM IPC, PAM↔sshd, storage backends)

50/50 v2.0 requirements now satisfied.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- docs/adversarial-review.md: Three-persona adversarial review (red team,
  enterprise architect, ops engineer) with cross-examination and prioritized
  findings. 10 findings catalogued, 1 P0 recommendation (test-mode release
  guard).
- Phase 16 VERIFICATION.md: INT-01..INT-04 all verified with test evidence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P0: Add compile_error! guard preventing test-mode in release builds
P0: Document dpop_required: strict as mandatory production default
P1: Pin ID token algorithm to JWKS-advertised algorithm (closes R-8)
P1: Enable nbf (not-before) validation for ID tokens
P1: Document JTI cache externalization deferral to v2.1 (SCALE-01)
P2: Add IPC connection semaphore (max 64) to prevent resource exhaustion

References: docs/threat-model.md §7, STRIDE analysis from Phase 16

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical fixes:
- SECURITY.md: config.toml → policy.yaml (file doesn't exist)
- CONTRIBUTING.md: license corrected to CC BY-NC-SA 4.0 (was Apache-2.0/MIT)
- CONTRIBUTING.md: Rust version 1.70 → 1.88 (matches Cargo.toml)
- CONTRIBUTING.md: remove docker-compose.dev.yaml refs (file doesn't exist)
- THREAT_MODEL.md: mark as superseded, point to threat-model.md v3.0
- README.md: link threat model to current version
- security-guide.md: remove nonexistent env vars (OIDC_VERIFY_TLS et al.)
- security-guide.md: fix break-glass config (users→accounts, method→requires)
- security-guide.md: PQC/ML-DSA-65 marked as planned, not implemented
- README.md: PQC marked as planned, not implemented

Medium fixes:
- installation.md: ChallengeResponseAuthentication → KbdInteractiveAuthentication
- installation.md: add mandatory break-glass setup section
- sudo-step-up.md: UNIX_OIDC_POLICY_PATH → UNIX_OIDC_POLICY_FILE
- deployment-patterns.md: fix username config schema (identity.transforms)
- examples/policy.yaml: add identity, break_glass, full security_modes
- docs/adr/README.md: add ADR-005, 005-alignment, 006 to index

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Break-glass auth events now log at CRITICAL, authentication/validation
failures at WARNING, and successful operations at INFO — replacing the
previous blanket INFO for all events. Adds AuditSeverity enum,
syslog_severity() method on AuditEvent, and comprehensive test coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes deserialization failures with Google and Azure configs that
omit preferred_username. Falls back to empty string for username
mapping when absent. Closes BUG-3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace fragile Debug-format string comparison with serde JSON
serialization, which produces stable canonical JOSE names (e.g.
'RS256'). Closes HARDEN-1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Block HS256/HS384/HS512 when JWKS key has no alg field to prevent
algorithm confusion attacks. Closes HARDEN-2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace DoD 5220.22-M naming with NIST SP 800-88 Rev 1 §2.4 (Clear),
which is the current authoritative standard. Closes HARDEN-5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds experimental post-quantum hybrid DPoP signing and verification
behind the `pqc` feature gate, per draft-ietf-jose-pq-composite-sigs-01.

Agent side:
- HybridPqcSigner with composite signature (2-byte len + ML-DSA-65 + ES256)
- Composite JWK thumbprint (RFC 7638 extended for COMPOSITE kty)
- Key persistence via export_ec_key/export_pq_seed + storage router
- Config wiring: CryptoConfig.enable_pqc selects signer at daemon startup
- build_dpop_message_with_alg/assemble_dpop_proof_composite helpers

PAM side:
- validate_composite_proof verifies both ML-DSA-65 and ES256 components
- Composite thumbprint computation matches agent-side
- Feature-gated: non-PQC builds reject ML-DSA-65-ES256 as UnsupportedAlgorithm

Cross-crate:
- Forward pqc feature from unix-oidc-agent to pam-unix-oidc dependency
- 5 integration tests: roundtrip, nonce, signature structure, binding, persistence

Also fixes:
- ADR-002: correct CIBA citation (was RFC 9126/PAR, now OpenID CIBA Core)
- ADR-007/protected_key.rs: correct zeroize docs (ecdsa-0.16 ZeroizeOnDrop
  is unconditional, no feature flag on p256 0.13)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add sweep_interval_secs (default 300s, min 60s) to TimeoutsConfig
- Add validation: sweep_interval_secs >= 60 prevents I/O thrashing
- Create daemon/sweep.rs with session_expiry_sweep_loop (async) and
  sweep_expired_sessions (sync): removes expired + corrupt .json files
- ENOENT on remove_file treated as success (concurrent PAM delete race)
- Non-.json files skipped; missing directory logs WARN and returns
- Register sweep module in daemon/mod.rs
- All 9 behavior tests (TDD) pass: defaults, expired/valid/corrupt/missing
  directory/non-json/ENOENT/env-override scenarios covered

Reference: SES-09
- Add sweep_interval and session_dir builder fields to AgentServer
- Add with_sweep_interval() and with_session_dir() builder methods
- serve_with_listener() spawns sweep::session_expiry_sweep_loop when both
  sweep_interval and session_dir are Some (defaults to None — opt-in)
- main.rs Gate 2 now captures full TimeoutsConfig; wires sweep_interval_secs
  and /run/unix-oidc/sessions/ into AgentServer builder chain
- cargo clippy --workspace -- -D warnings: clean
- cargo test -p unix-oidc-agent --lib: 159 passed, 0 failed

Reference: SES-09
- Add _mlock_guard: Option<MlockGuard> field to HybridPqcSigner
- Add new_inner() private constructor matching ProtectedSigningKey pattern:
  Box::new() for stable heap address, then mlock over full allocation
- Change generate() -> Self to generate() -> Box<Self> (MEM-05)
- Change from_key_bytes() -> Result<Self,...> to Result<Box<Self>,...> (MEM-05)
- Make MlockGuard and try_mlock pub(crate) in protected_key.rs for reuse
- Update load_or_create_pqc_signer in main.rs to return Box<HybridPqcSigner>
- Dereference Box at Arc::new() call sites (*pqc) for DPoPSigner coercion
- Add test_generate_returns_box, test_from_key_bytes_returns_box,
  test_ml_dsa_zeroize_on_drop, test_hybrid_signer_boxed_and_signs
- All 13 pqc_signer unit tests pass; workspace --features pqc green

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add parent_session_id to AgentRequest::StepUp with skip_serializing_if for backward compat
- Add parent_session_id to AgentResponseData::StepUpComplete with same compat semantics
- Update step_up_complete() constructor to accept parent_session_id parameter
- Add parent_session_id to PendingStepUp struct (threaded from request)
- Add parent_session_id to StepUpOutcome::Complete (threaded through poll_ciba)
- Thread parent_session_id through handle_step_up -> PendingStepUp -> StepUpOutcome -> StepUpComplete
- Read UNIX_OIDC_SESSION_ID in pam sudo.rs and include in StepUp IPC JSON
- Add AGENT_STEP_UP and AGENT_STEP_UP_COMPLETE/TIMED_OUT audit events (OBS-3)
- 5 new serde round-trip tests cover backward compat and full field serialization
- All existing tests updated for new struct fields
- Event 1 AGENT_AUTH: 3 failure paths (not_logged_in, no_access_token, sign_error)
  and 1 success path in GetProof handler; session_id='n/a' (pre-session context)
- Event 2 AGENT_REFRESH: success path in spawn_refresh_task backoff loop;
  session_id='n/a' (background task not tied to a PAM session IPC)
- Event 3 AGENT_SESSION_CLOSED: after cleanup_session completes; session_id from IPC
- Event 4 AGENT_STEP_UP: after PendingStepUp inserted; includes parent_session_id
- Event 5 AGENT_STEP_UP_COMPLETE / AGENT_STEP_UP_TIMED_OUT: in handle_step_up_result
  with both sudo_session_id and parent_session_id for end-to-end SIEM correlation
- All events include event_type, session_id, username, and outcome (OBS-1 requirement)
- timestamp provided automatically by tracing-subscriber JSON layer (not duplicated)
- Workspace builds clean with -D warnings; all 518 tests pass
- Create docs/standards-compliance-matrix.md (329 lines, 61 standards)
  covering RFCs, NIST SPs, FIPS, OpenID specs, IETF drafts,
  SOC2/PCI/HIPAA/FedRAMP controls, MITRE ATT&CK, and CIS Controls
- Cross-reference index mapping source files to standards
- Known gaps section with priority classification
- Close F-07 (ML-DSA mlock), OBS-1 (audit events), OBS-3 (session linking)
  as resolved by Phase 17
- Update STATE.md to mark standards matrix todo as complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…SSH tests)

Phase 18 — Blocker Fixes + E2E Infrastructure:
- BFIX-03: Device flow token polling now sends DPoP proof header per
  RFC 9449 §4.2 (fresh proof per poll iteration + refresh requests)
- INFR-01: docker-compose.e2e.yaml with Keycloak 26.4, aligned issuer
  URLs (KC_HOSTNAME=keycloak), NO TEST_MODE
- INFR-02: Keycloak health check uses /health/ready endpoint
- INFR-03: Sentinel script verifies TEST_MODE absence before E2E tests
- INFR-04: E2E realm JSON for Keycloak 26.4 (DPoP GA, device flow)
- BFIX-02: Agent binary installed on PATH in test-host-e2e container
- E2E policy.yaml with dpop_required: strict, real validation

Phase 19 — Playwright Device Flow Automation:
- PLAY-01: Playwright spec automates Keycloak device flow consent
- PLAY-02: Tmpfile coordination between shell poll loop and Playwright
- PLAY-03: CI-compatible headless config (no --no-sandbox)
- Shell orchestrator (run-device-flow-e2e.sh) coordinates both sides

Phase 20 — SSH E2E Test:
- E2E-01: test_keycloak_real_sig.sh validates full auth chain
- E2E-02: Token claims validation (issuer, username, audience)
- E2E-03: Negative tests (wrong realm, expired tokens, DPoP replay)

Formatting changes: cargo fmt applied to touched files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Dockerfile.build: rust 1.85→1.88 (MSRV), add libdbus-1-dev
- test-host: Ubuntu 22.04→24.04, add socat for localhost proxy
- Keycloak: 26.4→26.5.5 (realm file rename required by 26.5)
- Playwright: 1.52→1.58.2
- KC_HOSTNAME=localhost so browser+tokens use same origin
- socat proxies localhost:8080→keycloak:8080 inside test-host
- Rewrote E2E script to use unix-oidc-agent binary (real DPoP)
- Separated Linux binaries (target/release-linux) from native
- Verified selectors: #username, #password, #kc-login all present
- Full E2E pass: macOS agent→Keycloak 26.5.5→Playwright→DPoP token

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document D-Bus session bus snooping risks, Secret Service encrypted
session mitigation, residual risks (plain session fallback), and
operator hardening recommendations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…issuers(), issuer_by_url()

- Add AcrMappingConfig, GroupSource, GroupMappingConfig, IssuerConfig structs (MIDP-01..05)
- Add PolicyConfig.issuers: Vec<IssuerConfig> with serde default (backward compat)
- Add effective_issuers(): resolves issuers[] or synthesizes legacy OIDC_ISSUER path
- Add issuer_by_url(): trailing-slash-normalized lookup
- Duplicate issuer_url hard-fails at load_from() with ConfigError (MIDP design)
- MIDP-08 WARN logging for issuers missing acr_mapping or group_mapping
- 7 unit tests covering all multi-issuer config loading paths
- Reference fixture: test/fixtures/policy/policy-multi-idp.yaml (Keycloak + Entra)
prodnull and others added 22 commits April 11, 2026 02:29
Two release build failures:

1. libtss2-dev missing: tss-esapi-sys (TPM2 bindings) requires tss2-sys.pc
   Added libtss2-dev to x86_64 apt-get and libtss2-dev:arm64 to Cross.toml.
   Same pattern as libdbus-1-dev fix in previous commit.

2. Security gate false positive: PRMANA_TEST_MODE grep matched the env var
   name in the runtime check code, which is present in ALL builds. The true
   sentinel for test-mode compilation is new_insecure_for_testing (a function
   that only exists when --features test-mode is active). Grep updated to
   check only for that symbol.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
prmana-agent/src/crypto/pqc_signer.rs:
- ml-dsa 0.1.0-rc.8 moved from_seed to the KeyGen trait; import it
- Use MlDsa65::from_seed(&seed) via KeyGen trait (was SigningKey::from_seed)
- Use pq_key.signing_key().verifying_key() — verifying_key() is on
  ExpandedSigningKey in rc.8, not directly on the SigningKey wrapper

prmana-agent/src/storage/migration.rs:
- migration_pairs() declared pairs as let but pushes to it under #[cfg(pqc)]
  — needs mut; only surfaces when --all-features is active

.github/workflows/release.yml:
- Package artifacts step copied LICENSE-APACHE and LICENSE-MIT which do not
  exist; repo uses a single CC BY-NC-SA 4.0 LICENSE file

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Cold builds with --all-features take 40+ min on GH runners due to
TPM2/PQC/SPIRE compilation. Cache ~/.cargo and target/ keyed on
Cargo.lock to reduce subsequent runs to ~5 min.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
CWE-377 / CWE-379: write_with_restricted_perms() used .create(true) without
O_EXCL, allowing a local attacker to pre-create the tmpfile with 0666
permissions. The agent would write the OIDC access token into the attacker-
controlled file, preserving the world-readable permissions.

Fix: remove_file() clears any pre-existing file/symlink, then create_new(true)
(O_CREAT|O_EXCL) guarantees atomic exclusive creation with mode 0o600 set at
creation time. If an attacker wins the narrow race between remove and create,
create_new returns AlreadyExists — a localized DoS rather than token exfiltration.

Tmpfiles are keyed by PPID so two concurrent SSH sessions do not collide.
The token tmpfile is deleted immediately after reading (see run_ssh_askpass).

Discovered by Gemini security review, independently verified.
References: VULN-001, CWE-377, CWE-379

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Go stdlib (GO-2025-3750 — O_CREATE|O_EXCL inconsistency on Unix/Windows):
- go-oauth-dpop/go.mod: go 1.21 → 1.23
- dpop-cross-language-tests/go-test/go.mod: go 1.21 → 1.23

deny.toml — document accepted/deferred advisories with rationale:
- RUSTSEC-2026-0097 (rand 0.8.5 unsound with custom logger): accepted —
  prmana uses OsRng from rand_core directly, never rand::rng(); zero exposure
- RUSTSEC-2024-0436 (paste unmaintained): accepted — proc-macro only,
  no runtime risk
- RUSTSEC-2025-0134 (rustls-pemfile unmaintained): deferred — requires
  reqwest 0.11 → 0.12 upgrade (43 call sites + blocking feature refactor);
  tracked for a dedicated dependency update pass

reqwest 0.11 → 0.12 upgrade is intentionally deferred: the fix requires
refactoring 43 call sites and removing the blocking feature — a breaking
change that needs its own phase to do properly without introducing regressions.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…compile check

cargo test --all-features on a cold 2-core GH runner takes 45+ min because
it builds every optional crate (TPM2, PQC, SPIRE, YubiKey) from scratch.
Hardware-gated tests (TPM, YubiKey, SPIRE) are all #[ignore] anyway, so
--all-features adds only compilation cost, not test coverage.

Split into two steps:
1. cargo test --workspace (default features) — runs real tests in ~5 min
2. cargo check --workspace --all-features — verifies optional features compile

This keeps CI honest (all features still checked) while completing in a
reasonable time. The cargo cache will warm on first pass for future runs.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Previously 13 tests were #[ignore] and never ran in CI:
- 3x step_up_ipc: needed --test-threads=1 (now run sequentially)
- 2x headless_storage: needed Linux keyutils (now run with libkeyutils-dev)
- 1x storage router migration: needed ProjectDirs (works on Linux runner)
- 5x TPM swtpm tests: now run against swtpm Docker container
- 2x SPIRE workload API: now run against SPIRE Docker container

Still genuinely cannot run in CI (physical hardware required):
- 2x yubikey_signer: physical YubiKey needed (test locally with --ignored)
- 3x keyring_store/router: D-Bus session keychain (requires interactive session)

Workflow triggers on push/PR to main when agent/PAM source changes.
Uses shared Cargo cache key for efficient compilation across all 4 jobs.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…lume

TPM: ghcr.io/tpm2-software/swtpm requires authentication on GH Actions
  — add docker/login-action@v3 with GITHUB_TOKEN before compose up

SPIRE: bind-mount volume device /tmp/spire-agent/public must exist before
  docker compose can populate it — add mkdir -p before compose up
  — also add GHCR login since SPIRE images are on ghcr.io/spiffe/

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
cargo test --workspace was taking 40+ min cold because compilation and
test execution were merged in one step with no timeout guard.

Split into:
1. 'Build test binaries' — cargo test --workspace --no-run (compile only)
   No timeout — compilation time is unpredictable on cold cache
2. 'Run tests' — cargo test --workspace -- --test-threads=4 (15 min limit)
   If a test hangs, this step fails clearly after 15 min
3. 'Verify all-features compile' — 20 min limit

--test-threads=4 uses all 4 vCPUs (GH Actions runners have 4 cores,
not 2) for parallel test execution, cutting test run time significantly.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
send_ipc_message() wrote JSON and '\n' in two separate write_all calls.
When cargo test runs with --test-threads=4, the mock server can read the
JSON in the first syscall, respond, and close the stream before the
second write_all('\n') completes, causing EPIPE / Broken pipe.

Fix: concatenate JSON + newline into a single string and send in one
write_all call. The server receives a complete newline-delimited frame
atomically, eliminating the race window.

Affects: test_step_up_ipc_denied (pam-prmana sudo unit tests)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…DR-019

ADR-021 is now Accepted after a second-pass adversarial review. The ADR
splits SCIM hardening into Decision A (v1.0-blocking single-process
hardening — startup transport policy, request-shaping middleware, audit
chain integration, dry_run removal, systemd unit fix) and Decision B
(post-v1.0 helper-based privilege separation via AF_UNIX + SO_PEERCRED +
versioned RPC). Decision A is specified but not yet implemented; the ADR
now includes an Implementation Status section forbidding any "privilege
separation" marketing claim until Decision B ships.

Redlines applied from the second-pass review:

- Implementation Status section naming the gap between spec and code
- §A1: TLS fail-closed on missing/unreadable key material, rustls-via-
  axum-server named, TLS 1.3 default cipher policy, no silent fallback
- §A2: untrusted-peer behavior (strip X-Forwarded-*, audit
  forwarded_headers_stripped=true, never reject)
- §A3: new middleware layers (header size limit, TCP read-idle timeout,
  keep-alive idle timeout); discovery endpoint carveout for
  /ServiceProviderConfig and /Schemas; rate-limiter library choice
  (tower-governor + composite key extractor) flagged as multi-day work;
  defaults relabeled "illustrative"; Retry-After required on 429 and 503
- §A4: peer_addr/client_addr authoritativeness; rejection event types
  (rate_limited, payload_too_large, header_too_large, concurrency_exceeded,
  request_timeout, read_idle_timeout); audit stream locked to the ADR-010
  HMAC chain via a shared prmana-audit crate
- §A5: hard decision to delete dry_run field entirely; test migration to
  FakeAccountBackend via with_account_backend
- §A6: explicit 500-for-ops-failures vs 503-reserved-for-concurrency-and-
  helper contract
- §B1: reconciliation expansion (orphan mapping, orphan account, timeout)
  with x-prmana-* discovery fields
- §B2: prmana-scim is Linux-only; no launchd/rc.d units
- §B5: PrivateUsers=no is the default; IPAddressDeny= fail-closed via
  AssertKernelVersion= / ConditionSecurity=ip_filter
- §Effect on ADR-019: ADR-019 must carry inline supersession notes

ADR-019 is edited in three places to carry "Superseded by ADR-021"
inline notes on the capability-dropping (§4), rate-limiting (§5), and
TLS (§6) claims that describe behavior not present in the current code.

ADR index updated to reflect the Accepted status.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…-021

The README's "Production-ready security" bullet previously listed "Rate
limiting, JTI replay protection, structured audit logging" as a blanket
claim. Rate limiting exists in pam-prmana/src/security/rate_limit.rs
(PAM auth-attempt limits) but does NOT exist in prmana-scim — SCIM has
none of the request-shaping controls specified in ADR-021 §A3. The old
phrasing was ambiguous in a way that would lead a reader to assume
SCIM has protections it does not.

Changes:

- README "Production-ready security" → "PAM-path defenses" with an
  explicit pointer to the new SCIM Service Hardening Status subsection.
- README Security Design section → a scope-note callout at the top
  directing SCIM deployers to the new subsection before deploying.
- README new "SCIM Service Hardening Status" subsection enumerating
  what works today, what ADR-021 Decision A specifies but does not yet
  ship, what Decision B defers to post-v1.0, the explicit prohibition
  on "privilege separation" marketing until Decision B ships, and the
  Linux-only platform scope.
- SECURITY Defense-in-Depth bullet 4 "Minimal Privileges" → now scopes
  the claim to the PAM module and agent daemon and explains why
  prmana-scim runs as root.
- SECURITY new "SCIM Service Hardening Status" section including an
  Implication-for-deployment compensating-control checklist (loopback
  bind, reverse-proxy-terminated TLS, proxy-layer rate limits, network
  policy restriction) for operators running SCIM before DT-SCIM ships.
- SECURITY Supported Versions table: 0.1.x → 1.0.x (current release
  under ADR-021 Decision A), < 1.0 (pre-rename unix-oidc) unsupported.
- SECURITY pre-deployment checklist SCIM item expanded from a one-liner
  into an 8-point sub-checklist covering loopback bind, reverse-proxy
  TLS, network policy, reading ADR-021, and the anti-marketing-claim
  reminder.

The top-of-README "educational use only" disclaimer is retained
deliberately — v1.0 is still in the rc series and is not yet declared
ship-ready. Disclaimer will be revisited before cutting v1.0.0 GA.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…dlock

test_c1_get_token_prints_exec_credential was hanging indefinitely in CI,
causing Pre-release Tests on v1.0.0-rc1 (run 24286961692) to hit the
15-minute job timeout after all build and packaging jobs had succeeded.

Root cause: the test is #[tokio::test] and spawns a mock agent via
tokio::spawn, which runs on the current tokio runtime. The test then
called std::process::Command::new(binary).output() — a blocking call
that pins the current runtime thread. The mock-agent task never got
scheduled to accept the child process's connection, so the child
prmana-kubectl hung waiting for the socket, and .output() blocked
forever waiting for the child. Classic async/blocking deadlock.

Fix:
1. Use tokio::process::Command instead of std::process::Command so the
   async command does not block the runtime thread and the mock-agent
   task can make progress.
2. Wrap the .output() await in a tokio::time::timeout of 30 seconds so
   any future regression in the agent handshake fails fast with a
   clear message instead of eating the 15-minute job budget.

Verified locally: test passes in 0.28s (vs. forever before the fix).

This unblocks cutting v1.0.0-rc2 after v1.0.0-rc1's Pre-release Tests
failure.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The Check job in ci.yml (cargo fmt --all --check) was failing on main
because ~17 files across pam-prmana, prmana-agent, and prmana-kubectl
had accumulated formatting drift that was not caught by per-commit
pre-push checks. This commit runs cargo fmt --all on a clean tree and
commits only the formatting deltas, no functional changes.

Files touched (all formatting-only):
- pam-prmana: device_flow/client.rs, lib.rs, oidc/validation.rs,
  tests/adversarial_pentest.rs
- prmana-agent: askpass.rs, auth_code.rs, main.rs, metrics.rs,
  storage/migration.rs, storage/router.rs, tests/spire_integration.rs
- prmana-kubectl: commands.rs, exec_credential.rs, ipc_client.rs,
  kubeconfig.rs, socket_path.rs, tests/integration.rs

Verified clean with cargo fmt --all --check.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
… deadline

Two unrelated main-CI unblocks bundled together:

1. .github/workflows/kubectl-e2e.yml — "Unit tests (prmana-kubectl +
   audience isolation)" was failing at "Build workspace" because the
   runner did not have libdbus-1-dev installed, so libdbus-sys v0.2.7's
   pkg_config build script failed. Same issue and same fix as the one
   applied to release.yml in commit 8ad1523. Adds the same apt install
   step to both jobs in kubectl-e2e.yml: unit-tests and kubectl-e2e.

2. prmana-agent/tests/daemon_lifecycle.rs — test_daemon_lifecycle was
   flaking on GHA macos-aarch64 with "Daemon did not exit within 5
   seconds after Shutdown". The 5-second deadline measured only the
   gap between sending the shutdown command and the parent process's
   try_wait() observing the exit. On slow GHA macos-aarch64 runners
   this budget is too tight even though the daemon does exit correctly
   (verified locally — 5.64s end-to-end on a fast Apple Silicon
   machine, right at the old deadline).

   Bumps the deadline to 30 seconds with a comment explaining the
   runner-speed sensitivity and why 5s was the wrong threshold. The
   new deadline is still strict enough to catch a genuine hang (the
   expected exit latency is sub-second on any realistic machine) but
   absorbs GHA runner variance.

Both fixes are independent of rc2 tag — this is main-branch CI hygiene
to make future PRs land cleanly.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…nd trait

Substantial hardening pass on prmana-scim covering authorization, state
durability, and testability. This is the "recent hardening work" ADR-021
§Context refers to as already complete — committing it explicitly.

Authorization (prmana-scim/src/auth.rs):

- Adds a required_entitlement check to AuthMode::Validated alongside the
  existing iss/aud/exp/signature validation. Accepts the configured
  entitlement value from any of the five common claim shapes IdPs
  actually ship: top-level scope/scp/roles, realm_access.roles,
  resource_access[oidc_audience].roles. A StringOrList enum handles
  both space-separated and array-shaped scope/scp.
- Returns 401 + "Insufficient privileges" when the claim is missing,
  with a WARN log line carrying issuer/audience/required_entitlement.
- Tests (positive + negative):
  test_validated_mode_rejects_token_missing_required_entitlement,
  test_validated_mode_accepts_required_scope,
  test_validated_mode_accepts_required_role.

Persistent state (prmana-scim/src/provisioner.rs):

- Introduces AccountBackend trait with SystemAccountBackend (production:
  subprocess useradd/usermod/userdel + NSS lookup via `uzers`) and
  FakeAccountBackend (tests). Provisioner::new() returns the production
  path; tests use with_account_backend to inject the fake.
- {SCIM id <-> ScimUser} mapping persists to a JSON state file:
  atomic temp+rename, mode 0600 on file, mode 0700 on parent dir,
  UUID-suffixed temp name. Load enforces persisted SCIM id matches the
  map key and rejects drift.
- create_user now rejects if a system account with that name already
  exists in NSS (not just the in-memory store).
- replace_user rejects userName changes via UsernameImmutable error
  (Unix usernames are load-bearing; SCIM rename is unsafe).
- replace_user preserves original `created` timestamp in meta.
- list_users filters out SCIM entries whose backing system account has
  disappeared (drift detection).
- Tests (positive + negative): persists-across-restart, duplicate-for-
  existing-system-user, replace-updates-backend-and-state, replace-
  nonexistent, delete-after-restart, list-filters-missing-accounts,
  dry_run-keeps-persisted-state, plus existing username validation.

Config (prmana-scim/src/config.rs):

- Adds required_entitlement: String (default "scim:provision").
- Adds state_file: String (default "/var/lib/prmana/scim-users.json").

Startup (prmana-scim/src/main.rs):

- Refuses to start if required_entitlement is empty.
- Logs issuer + required_entitlement at startup.
- Propagates Provisioner::new() state-file errors as fatal.

Note: dry_run remains a runtime config field here. ADR-021 §A5 (Accepted
2026-04-11) now specifies deleting it entirely; that removal is tracked
as phase DT-SCIM-05 and is deliberately not part of this commit.

Verified locally on macOS: cargo fmt clean, cargo clippy
--all-targets --all-features -- -D warnings clean, 44/44 prmana-scim
tests passing.

Co-Authored-By: Codex
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Unblock and green-light the full CI matrix:
- Pin all GitHub Actions to commit SHAs with least-privilege tokens
- Fix Keycloak E2E: pam_env token delivery, auth-phase chain
- Fix TPM: swtpm native install, TCTI mssim, KDF scheme, idempotent provision
- Fix SPIFFE: camelCase flags, distroless sidecar, test filter
- Fix CIBA: binding_message encoding, defensive script rewrite, channel skip
- Fix Auth0: preferred_username multi-key lookup
- Serialize Check unit tests to eliminate env-var race

Namespace audit (unix-oidc → prmana):
- Rename Chef cookbook and Puppet module to prmana
- Remove legacy Ansible unix_oidc role
- Rename logrotate config, update CODEOWNERS team name
- Fix prmana.dev → prmana.com in docs and tests
- Update presentation product name

Also includes:
- HTTPS endpoint policy + canonical runtime socket paths
- Fail-closed installer signature verification
- Claude/Gemini file-based dialogue sync script

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove || true masks from 3 E2E tests in CI (dpop-nonce, session
  lifecycle, systemd) — tests must fail visibly now that CI is green
- Enforce rustdoc -D warnings (was -W) and fix all 12 doc warnings:
  bare URLs, unresolved intra-doc links, unclosed HTML tags
- Verified: cargo fmt, clippy -D warnings, and rustdoc -D warnings
  all pass cleanly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Product boundary (locked):
- OSS (Apache-2.0): prmana-core, pam-prmana, prmana-agent
  SSH login, device flow, auth code, DPoP, software/YubiKey/TPM signing,
  local policy, break-glass, local audit
- Enterprise (proprietary): enterprise/
  SCIM, kubectl, sudo step-up, approval workflows, CIBA, multi-IdP
  failover, token exchange, SPIFFE/SPIRE, fleet/posture/compliance

Structural changes:
- New crate: prmana-core (OIDC discovery + JWKS types)
- prmana-agent runtime dependency on pam-prmana fully severed
  (now depends on prmana-core; pam-prmana retained as dev-dep only)
- Enterprise modules excised from pam-prmana:
  approval/, ciba/, evidence.rs, sudo.rs, bin/evidence_export.rs
- Enterprise modules excised from prmana-agent:
  failover.rs, exchange.rs, spire/, attestation_pop.rs, spire_signer.rs
- Enterprise crates relocated: prmana-scim, prmana-kubectl → enterprise/crates/
- Enterprise docs, deploy templates, test fixtures, packaging → enterprise/
- License: Apache-2.0 (OSS), proprietary (enterprise/)
- README rewritten for OSS product
- CLAUDE.md updated with OSS/enterprise boundary section

All tests pass (866 passed, 0 failed). Clippy clean. Fmt clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove demo scenarios that showcase enterprise features (sudo step-up,
token exchange, policy configuration, audit events). Remove CI workflows
for enterprise infrastructure (fleet, kubectl-e2e, ARM64 AMI, MAC policy,
AWS platform tests).

Kept: provider-tests.yml (Keycloak is OSS; commercial IdP tests are
secrets-gated and won't run on community forks).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bumps the rust-all group with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [thiserror](https://github.com/dtolnay/thiserror) | `1.0.69` | `2.0.18` |
| [reqwest](https://github.com/seanmonstar/reqwest) | `0.11.27` | `0.12.28` |
| [libc](https://github.com/rust-lang/libc) | `0.2.184` | `0.2.185` |
| [base64](https://github.com/marshallpierce/rust-base64) | `0.21.7` | `0.22.1` |
| [syslog](https://github.com/Geal/rust-syslog) | `6.1.1` | `7.0.0` |
| gethostname | `0.4.3` | `1.1.0` |
| [getrandom](https://github.com/rust-random/getrandom) | `0.3.4` | `0.4.2` |
| [sha1](https://github.com/RustCrypto/hashes) | `0.10.6` | `0.11.0` |
| [sha2](https://github.com/RustCrypto/hashes) | `0.10.9` | `0.11.0` |
| [hmac](https://github.com/RustCrypto/MACs) | `0.12.1` | `0.13.0` |
| [directories](https://github.com/soc/directories-rs) | `5.0.1` | `6.0.0` |
| [dirs](https://github.com/soc/dirs-rs) | `5.0.1` | `6.0.0` |
| [cryptoki](https://github.com/parallaxsecond/rust-cryptoki) | `0.7.0` | `0.12.0` |


Updates `thiserror` from 1.0.69 to 2.0.18
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](dtolnay/thiserror@1.0.69...2.0.18)

Updates `reqwest` from 0.11.27 to 0.12.28
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](seanmonstar/reqwest@v0.11.27...v0.12.28)

Updates `libc` from 0.2.184 to 0.2.185
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.185/CHANGELOG.md)
- [Commits](rust-lang/libc@0.2.184...0.2.185)

Updates `base64` from 0.21.7 to 0.22.1
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](marshallpierce/rust-base64@v0.21.7...v0.22.1)

Updates `syslog` from 6.1.1 to 7.0.0
- [Commits](Geal/rust-syslog@6.1.1...7.0.0)

Updates `gethostname` from 0.4.3 to 1.1.0

Updates `getrandom` from 0.3.4 to 0.4.2
- [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md)
- [Commits](rust-random/getrandom@v0.3.4...v0.4.2)

Updates `sha1` from 0.10.6 to 0.11.0
- [Commits](RustCrypto/hashes@sha1-v0.10.6...sha1-v0.11.0)

Updates `sha2` from 0.10.9 to 0.11.0
- [Commits](RustCrypto/hashes@sha2-v0.10.9...sha2-v0.11.0)

Updates `hmac` from 0.12.1 to 0.13.0
- [Commits](RustCrypto/MACs@hmac-v0.12.1...hmac-v0.13.0)

Updates `directories` from 5.0.1 to 6.0.0
- [Commits](https://github.com/soc/directories-rs/commits)

Updates `dirs` from 5.0.1 to 6.0.0
- [Commits](https://github.com/soc/dirs-rs/commits)

Updates `cryptoki` from 0.7.0 to 0.12.0
- [Changelog](https://github.com/parallaxsecond/rust-cryptoki/blob/main/CHANGELOG.md)
- [Commits](parallaxsecond/rust-cryptoki@cryptoki-0.7.0...cryptoki-0.12.0)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-version: 2.0.18
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: rust-all
- dependency-name: reqwest
  dependency-version: 0.12.28
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-all
- dependency-name: libc
  dependency-version: 0.2.185
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rust-all
- dependency-name: base64
  dependency-version: 0.22.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-all
- dependency-name: syslog
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: rust-all
- dependency-name: gethostname
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: rust-all
- dependency-name: getrandom
  dependency-version: 0.4.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-all
- dependency-name: sha1
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-all
- dependency-name: sha2
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-all
- dependency-name: hmac
  dependency-version: 0.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-all
- dependency-name: directories
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: rust-all
- dependency-name: dirs
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: rust-all
- dependency-name: cryptoki
  dependency-version: 0.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: rust-all
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot @github
Copy link
Copy Markdown
Contributor Author

dependabot Bot commented on behalf of github Apr 13, 2026

Labels

The following labels could not be found: dependencies, rust. Please create them before Dependabot can add them to a pull request.

Please fix the above issues or remove invalid values from dependabot.yml.

@dependabot @github
Copy link
Copy Markdown
Contributor Author

dependabot Bot commented on behalf of github Apr 13, 2026

This pull request was built based on a group rule. Closing it will not ignore any of these versions in future pull requests.

To ignore these dependencies, configure ignore rules in dependabot.yml

@dependabot dependabot Bot deleted the dependabot/cargo/rust-all-2b0126d790 branch April 13, 2026 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant