fix(obliterate): scrub content-store blobs and log entries for the path (P0-1) - #141
Merged
Merged
Conversation
`jk obliterate` only shredded the working file; every prior version stayed recoverable from .januskey/content (gzip blobs) and the operation log, and `jk undo` could resurrect it. - reversible-core MetadataStore: add purge_path / purge_path_entries (remove entries whose path or path_secondary matches, persist, return the referenced hashes) and referenced_hashes over surviving entries. - obliteration: add obliterate_path(jk, manager, path, ..) which shreds the working file if present, shreds every blob referenced only by the path's entries (blobs deduplicated with another path are kept), then purges the entries. Blobs are shredded before the log is purged so a failed shred leaves the command re-runnable. Stored paths are matched after normalisation (relative to root, parent canonicalised). Each shred is recorded in .januskey/obliterations.json via the new ObliterationManager::record_proof; chaining into the keyed AuditLog is deferred to J2-3. Removes the TODO(product). - jk obliterate: use obliterate_path when the working dir has a .januskey/ store (also handles delete-then-obliterate, where the working file is already gone); exit non-zero if any path failed. - tests/obliteration_cas_test.rs: positive-controlled checks that blobs, log entries, decompressed plaintext and undo are gone; shared blob kept and other path still undoable; delete-then-obliterate; CLI end to end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches📝 Generate docstrings
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 |
hyperpolymath
enabled auto-merge (squash)
October 2, 2026 12:14
hyperpolymath
disabled auto-merge
October 2, 2026 12:14
hyperpolymath
added a commit
that referenced
this pull request
Oct 2, 2026
Unblocks the **required** `scan / gitleaks` check, which is red on `main` (0613e69) and therefore on every open januskey PR, including ULTRAPLAN P0-1 (#141) and P0-2 (#140). ## Cause The estate secret scanner mirrors `.adoc` files, which default gitleaks never scans. Its `generic-api-key` rule matched two **example** UUIDs in a JSON sample in `docs/security/KEY_LIFECYCLE.adoc`: - :487 `"key_id": "123e4567-e89b-12d3-a456-426614174000"` - :488 `"new_key_id": "789e0123-e89b-12d3-a456-426614174000"` These are documentation placeholders, not secrets. ## Change Both values are replaced with obvious low-entropy placeholders (`00000000-0000-4000-8000-000000000001` and `…0002`). One file, 2 lines changed. ## Evidence Run locally with gitleaks and the estate baseline `standards:config/gitleaks/estate-baseline.toml` (origin/main): | Input | Result | |---|---| | original file (positive control) | `leaks found: 2`, generic-api-key at lines 487 and 488, the same two lines CI reports | | edited file | `no leaks found` | | all `.adoc` files in the repo, mirrored | `no leaks found` | 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ULTRAPLAN P0-1:
jk obliteratenow removes the content history, not just the working file.Defect (0613e69)
obliterate_file(obliteration.rs:352) shredded only the working file.cmd_obliteratenever opened the JanusKey store. Every prior version therefore survived as a gzip blob in the content store, and the log entries kept naming the path, sojk undocould bring the "obliterated" content back. The code knew this:TODO(product)at :349.Change
reversible-core/metadata.rs:purge_pathremoves every log entry whosepathorpath_secondarymatches, saves, and returns the hashes those entries referenced.purge_path_entriesdoes the same but returns the removed entries.referenced_hashes()returns the refcount set.OperationLogdoc now names its two non-append exceptions, prune and obliteration.januskey-cli/obliteration.rs:obliterate_pathworks in four steps:ObliterationManager::obliterate. Shared blobs are kept.Blobs go before the log, so a failed shred leaves the entries in place and the command can be re-run. Stored path spellings are normalised against the store root, which still works after the file has been deleted. Every shred is recorded in
.januskey/obliterations.json. TheTODO(product)is removed.main.rs:cmd_obliterateusesobliterate_pathwhen.januskey/is initialised and keeps the old behaviour otherwise. It now exits non-zero if any path fails; before, it always returned 0.Evidence
cargo test --workspace: 98 → 106 passed, 0 failed. reversible-core unit tests went 12 → 15, plus the newtests/obliteration_cas_test.rswith 5 tests.The main test has a positive control. Blobs are gzip-compressed, so a naive byte-grep would pass vacuously.
content_pathexists,retrieve()returns the expected V0 and V1 plaintext, and a decompressing walk of.januskey/finds both markers.retrieveerrs;undoreturns Err.The other four tests:
jk init,modify,obliterate, thenjk undoprints "Nothing to undo", with the marker walk before and after.Mutants, each applied and then reverted:
Clippy warnings in the touched files are unchanged (main 19, obliteration 4, metadata 1). The new test file has 0. rustfmt is clean on all touched files.
Residual traces, stated rather than hidden
obliterations.jsonkeeps each shredded blob's sha256. For low-entropy content, someone could confirm a guess against it. This is a candidate for J2-3: record a keyed MAC instead of the bare hash.metadata.jsonis rewritten withfs::write, so its old bytes, which include the path, may stay in freed blocks. This is the same copy-on-write/SSD caveat the file header already gives.transactions/keeps operation IDs that now point to nothing. They are IDs only, with no content.AuditLogneeds an unlocked attestation key, so it is deferred to J2-3.normalise_pathmakes onecanonicalizecall per log entry in each of about four passes, so roughly 40k calls at max_history=10000. Correct, but optimisable later.🤖 Generated with Claude Code
https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4