Skip to content

SecretRead migration + threat-model refresh for core-dump, handle wrap, bridge#18

Merged
jgowdy-godaddy merged 5 commits intomainfrom
docs/design-cleanup
Apr 17, 2026
Merged

SecretRead migration + threat-model refresh for core-dump, handle wrap, bridge#18
jgowdy-godaddy merged 5 commits intomainfrom
docs/design-cleanup

Conversation

@jgowdy-godaddy
Copy link
Copy Markdown
Contributor

Summary

  • Typed SecretRead migrationtoken_source.rs call sites that compared strings against REDACTED_PLACEHOLDER now match on SecretRead::{Present, Redacted, Absent} via the new get_read method. The (vanishingly unlikely) collision where a stored token-source state whose bytes happened to equal "<redacted>" would be misclassified is closed structurally.
  • LaunchRequest.env_scrub_patterns field added to the LaunchRequest { ... } construction in passthrough.rs, defaulted to Vec::new() — no behavior change for existing flows. Callers that want to strip inherited NPM_TOKEN_* can opt in via with_env_scrub(["NPM_TOKEN_*"]).
  • THREAT_MODEL refresh:
    • Child-process core-dump section rewritten around the new pre_exec RLIMIT_CORE = 0 hook in libenclaveapp's launcher.
    • .handle plaintext stale reference replaced — the SE handle is now AES-256-GCM sealed under a Keychain-held key (com.libenclaveapp.<app> / <label>).
    • WSL bridge section updated for the which-fallback removal and Authenticode-presence check.
    • Top residual risks Add CLAUDE.md with integration type documentation #4 updated — the old .handle plaintext entry is gone; inherited parent-env NPM_TOKEN_* is now called out with the opt-in env-scrub helper as the mitigation.

Depends on

Test plan

  • cargo check --workspace clean.
  • cargo fmt --all -- --check clean.
  • cargo clippy --workspace --all-targets -- -D warnings clean.
  • cargo test --workspace — 252 tests passing, 0 failing.
  • inspection_reacquires_redacted_provider_state_when_prepared_marker_exists test migrated from the string-sentinel simulation to mark_redacted(), which is the semantically-correct way to inject the Redacted variant.

jgowdy added 5 commits April 17, 2026 09:55
Depends on libenclaveapp's new SecretRead enum.

token_source.rs:
- Three call sites that inspected the token-source state via
  SecretStore::get and compared against REDACTED_PLACEHOLDER now call
  get_read and match on SecretRead::{Present, Redacted, Absent}.
- The collision where a real stored state equal to the literal
  "<redacted>" would be misclassified as redacted is closed: the
  typed API returns Present even if the bytes match the sentinel.
- A test that previously wrote REDACTED_PLACEHOLDER into
  MemorySecretStore to simulate inspection-mode redaction now uses the
  new mark_redacted() helper, which is the semantically-correct way
  to inject the Redacted variant without the string sentinel.

passthrough.rs:
- LaunchRequest struct gained the env_scrub_patterns field; pass
  Vec::new() as the default (no scrubbing unless a caller opts in).
  Callers that want to strip inherited NPM_TOKEN_* can chain
  with_env_scrub (["NPM_TOKEN_*"]) on the returned LaunchRequest.

No user-visible behavior change for existing flows. The "\<redacted>\"
sentinel collision was vanishingly unlikely for npm tokens (they
start with npm_) but the typed API removes it structurally.
- 'Core dump / swap of the npm process' rewritten around the new
  pre_exec RLIMIT_CORE = 0 hook on the spawned child (landed in
  libenclaveapp's enclaveapp-app-adapter::launcher).
- '.handle plaintext on macOS' rewritten — it's no longer plaintext.
  libenclaveapp now AES-256-GCM wraps the SE dataRepresentation under
  a per-label Keychain key with service com.libenclaveapp.<app>.
  Stale fix-macos.md reference removed.
- 'WSL bridge' updated: which-fallback is gone and Authenticode-presence
  check is enforced before spawn.
- 'Top residual risks' list — the stale item #4 (.handle plaintext) is
  replaced with inherited parent-env NPM_TOKEN_* as a call-out for the
  opt-in env-scrub helper.
Integration tests spawn `npmenc` child processes via assert_cmd with
$HOME overridden to a tempdir. After libenclaveapp #68 the Swift
bridge routes all SecItem* calls through an explicitly-opened login
keychain via kSecUseKeychain. On GitHub Actions macOS runners that
keychain re-locks between jobs, so SecItemAdd blocks waiting for a
GUI unlock prompt that never arrives — the job hangs indefinitely.

Unlock the keychain up front with the empty password that the
runner image installs, and bump the auto-lock idle timeout so the
keychain doesn't re-lock mid-test.
cargo test --workspace on the macos-latest runner was hanging 30+
minutes in the first integration test because an unsigned binary's
SecItemAdd against the runner's login keychain blocks on an ACL
confirmation prompt no one can answer.

libenclaveapp #71 added a runtime escape hatch:
ENCLAVEAPP_MOCK_STORAGE=1 forces create_encryption_storage to
return an in-memory MockEncryptionStorage. Set it on macOS only —
Linux and Windows run against their real backends. The library's
own enclaveapp-apple tests still exercise the real keychain on
macOS CI.

Drop the earlier keychain-unlock step — it wasn't the locked
state that was blocking, it was the ACL prompt, and the mock
path sidesteps both.
@jgowdy-godaddy jgowdy-godaddy merged commit ba8fb17 into main Apr 17, 2026
5 of 6 checks passed
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.

2 participants