Catches the workspace and the extension manifests up to the v0.5.x
release line (was still showing 0.2.0).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The groups-cache opt-out is a developer debugging knob, not a
user-facing config. Gating the env-var lookup behind cfg!(debug_assertions)
makes release builds ignore the variable; the optimiser removes the
lookup entirely, so the variable name doesn't appear in release binary
strings output.
Doc-comments updated to reflect the new behaviour.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
verify_commit previously loaded devices.json/revoked.json and threw
both away, accepting any commit whose stderr contained "GOODSIG" or
"Good signature". This left device registration and revocation as
no-ops: unregistered keys could push, revoked keys kept working.
The fix:
- Build a temp gpg.ssh.allowedSignersFile from devices.json at the
commit, passed via GIT_CONFIG_COUNT/KEY/VALUE env (no global git
config mutation).
- Run git verify-commit --raw and parse SHA256 fingerprint from stderr
regardless of exit code (SSH git outputs the "Good" line even for
keys not in allowed-signers, with "No principal matched" + exit 1).
- Check revoked.json FIRST: reject if committer_ts >= revoked_at;
accept historical commits (committer_ts < revoked_at).
- Reject if fingerprint is not in active devices.json.
- Bootstrap: accept only when BOTH devices.json AND revoked.json are
empty/absent (not just devices.json alone).
Acceptance: 4 integration tests covering the matrix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wraps ssh-key's PublicKey::fingerprint(HashAlg::Sha256). Output format
matches ssh-keygen -lf and git verify-commit --raw stderr
(SHA256:<43-char base64>). Used by the upcoming relicario-server
verify-commit rewrite (audit S1).
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- register_device() generates signing + deploy keypairs via core device
module, stores them in DEVICE_STATE (once_cell Lazy<Mutex>), and
returns only public keys to JS
- sign_for_git() signs data using the internal signing key
- get_device_info() returns name and public keys; returns null if not
registered
- clear_device() zeroes and drops device state (logout / re-registration)
- Removed generate_device_keypair() which exposed raw private key bytes
Fixes audit I5: private key material no longer crosses the WASM boundary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove device from devices.json
- Append to revoked.json with timestamp and revoked_by
- Delete Gitea deploy key (best-effort, warns if env vars missing)
- Always commit both devices.json and revoked.json together
- Print revoked signing public key for audit confirmation
- Guard against revoking the current device (would lose push access)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- verify-commit command checks signature against devices.json
- generate-hook outputs installable pre-receive script
- Foundation for server-side enforcement
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
OpenSSH-format keypair generation, signing, and verification.
Foundation for device authentication.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
per_vault_soft_cap_bytes and per_vault_hard_cap_bytes were defined in
VaultSettings but never checked. Now enforced in cmd_attach with
warning at soft cap, error at hard cap.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Control characters (newlines, tabs) in item titles corrupted git log
output. Now strips control chars and truncates to 50 chars.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Crafted .relbak files with IDs like "../../.bashrc" could escape the
target directory. Now validates that item/attachment IDs are hex-only
via is_valid() before any fs::write.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
RELICARIO_TEST_PASSPHRASE and friends were checked in production code,
exposing the passphrase via /proc/<pid>/environ and shell history.
Now only compiled into debug binaries via cfg(debug_assertions) helper
functions. Release builds compile the helpers to return None, so the
env var names are absent from the release binary (verified via strings).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HOTP requires incrementing and persisting the counter after each use.
Without vault-save machinery in compute_totp_code, HOTP would desync
immediately. Now returns HotpNotSupported error.
TOTP and Steam codes continue to work.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- AttachmentId now uses 16 bytes of SHA-256 (128 bits) instead of 8,
requiring ~2^64 work for birthday collision instead of ~2^32.
- Added is_valid() to ItemId and AttachmentId for path traversal
prevention during backup restore.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backup KDF was passing raw passphrase bytes to Argon2id without NFC
normalization, causing cross-platform restore failures for non-ASCII
passphrases (macOS NFD vs Linux NFC).
Now matches derive_master_key behavior from crypto.rs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Brand name uses capital R in user-facing text — extension UI strings,
CLI clap help / descriptions / error prose, markdown docs. Lowercase
preserved for the binary command, crate names, npm package, file
paths, env vars, and code identifiers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- import_lastpass.rs: note that password and extra are intentionally
not trimmed (leading/trailing whitespace is significant for both).
- cmd_import_lastpass: document the coupling between the
ImportWarning message strings and the CLI summary's "skipped"
filter — partial-import warnings (TOTP/URL) must not contain
the word "skipped".
Comment-only; no behavior change. Catches I1 and M5 from the
final code review without taking on the cross-cut WarningKind
enum refactor (deferred to a follow-up if it ever ships).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Returns { items: [Item], warnings: [ImportWarning] } as a JSON
string. The items already have fresh IDs + timestamps; the SW
caller encrypts and writes them through the existing
item_encrypt + manifest_encrypt bridges.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Unlocks the vault, parses the CSV, encrypts each item, writes
items/<id>.enc and manifest.enc, then a single
`git add … && git commit` covers all of them. Stderr progress
every 50 items + final summary. Exit non-zero only when zero
items imported.
Adds the Import command group with a Lastpass subcommand.
Stub returns `not implemented` so the help text is reachable
ahead of the body landing in Task 8.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bad URLs in login rows downgrade to url: None with a warning
rather than skipping the row. Header mismatches (extra columns,
wrong order) surface ImportCsvHeader. Quoted commas, multi-line
extra, unicode all parse cleanly via the csv crate's defaults.
Rows with url == "http://sn" map to SecureNoteCore with extra
copied verbatim into the body. LastPass-packed structured data
(credit cards, addresses) flows through unparsed — users can
re-categorize manually post-import.
SecureNote rows skip the password-required check that applies
to Logins.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Successful base32 decode attaches a SHA1/6/30s Totp config to
LoginCore.totp. Bad base32 emits a warning and imports the login
without TOTP rather than skipping the row entirely.
Refactors map_row to return (Option<Item>, Option<ImportWarning>)
so a single row can produce both an item and a warning.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Map LastPass grouping/fav/extra columns to relicario item metadata.
Grouping becomes item.group, fav="1" sets item.favorite, extra becomes item.notes.
Multi-line extra via CSV quoting round-trips correctly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pins the parse_lastpass_csv signature and ImportWarning shape.
A single LastPass row with name/url/username/password round-trips
to a Login item with a freshly-minted ID. Header validation
rejects shape mismatches with a clear message.
TOTP, grouping, fav, SecureNote rows, and error paths land in
Tasks 3-6.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds csv = "1" to relicario-core; introduces
ImportCsvHeader and ImportCsvFormat. Foundation for the
import_lastpass module landing in Task 2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Locks the singular vs plural transition (1 minute ago vs 2 minutes
ago) and each bucket boundary (59→60s minutes, 3599→3600s hours,
86400→86400×2 days, etc.) so future tweaks can't silently regress
the user-facing labels.
Drop the dead `stdout.contains("last export:")` + `.to_lowercase()` fallback
in status_shows_last_backup_line and status_shows_recent_backup_after_export;
assert `stdout.contains("Last export:")` verbatim instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>