Commit graph

12 commits

Author SHA1 Message Date
Brian Majewski
17772dfec6 fix: validate-before-mutate in recipients add/rm + verify messaging + doc/UX polish (EGB-283)
- Fix 1 (IMPORTANT): _recipients_rm and _recipients_add now call _load_recipients
  BEFORE any mutation. A hand-corrupted recipients.txt dies at validation, leaving
  the file untouched — prevents inconsistent state where the file is changed but
  blobs are not re-encrypted. On a legacy store (no recipients.txt), _load_recipients
  succeeds via the derived-pubkey path so the bootstrap path still works.

- Fix 2 (MINOR): Guard _check_blob_recipient_count behind a successful decrypt in
  both _verify_all and _verify_project — an undecryptable blob no longer produces a
  spurious "encrypted to 0 recipients" finding. Reword _verify_all summary to
  "failed (decrypt or recipient-count)" since both failure modes now increment the
  counter.

- Fix 3 (MINOR): Correct README offboarding comment from "New blobs are no longer
  readable" (contradicts the re-encrypt of EVERY blob) to "Existing blobs are
  re-encrypted; the removed key can no longer decrypt them."

- Fix 4 (MINOR): cmd_reencrypt prints an advisory when no recipients.txt exists
  (single-key store), so the operator knows they can add teammates.

- Fix 5 (MINOR): Test coverage for ambiguous-name rm refusing to remove when
  multiple recipients share a --name label.

Tests: 5 new tests in test/recipients.bats (34 total, all pass). Full suite
276 tests: 5 known pre-existing failures (3 mode-600/stat, 2 jq-PATH), none new.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:37:56 -07:00
Brian Majewski
b09f94c92f test: pin security fixtures to the validation rail (EGB-283)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:21:33 -07:00
Brian Majewski
28f44f043c test: recipients.txt security regression fixtures + dangling-symlink fix (EGB-283)
Swap _load_recipients check order so [ -L ] (symlink) runs before [ ! -e ]
(missing), closing the gap where a dangling symlink bypassed the refusal
and silently fell back to legacy single-key mode.

Add 6 SECURITY-tagged fixtures to test/recipients.bats: dangling-symlink
refused (the ordering gap), shell-metachar injection (no execution), extra-
age-flag-looking line, control/ANSI chars, embedded whitespace via add, and
symlinked file refused on add. All 6 pass immediately after the ordering fix;
the existing rails (_validate_age_recipient, -L checks) were already tight
enough that only the production swap was needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:17:28 -07:00
Brian Majewski
e4cd524483 feat: verify asserts blob recipient-count matches recipients.txt (EGB-283)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:10:53 -07:00
Brian Majewski
ae52b8c077 test: restore bootstrap-path + which-legacy coverage (EGB-283)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:06:57 -07:00
Brian Majewski
50b73083f1 feat: init born-multi recipients.txt + which recipients line (EGB-283)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:03:20 -07:00
Brian Majewski
cad5b66f77 feat: secrets recipients rm with lockout guards (EGB-283)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 12:56:50 -07:00
Brian Majewski
4ba0234bd7 fix: harden recipients add --name validation (EGB-283)
Require a non-empty argument after --name (dies if it is the last token)
and reject whitespace-only labels that would write a blank comment line.
Two regression tests added to test/recipients.bats.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 12:53:27 -07:00
Brian Majewski
c262661030 feat: secrets recipients add (EGB-283)
Add `secrets recipients add <age1...> [--name <label>]`: validates the
age key and optional display name, bootstraps recipients.txt with the
local pubkey on a legacy store (keeping the operator as a recipient),
rejects duplicates, appends the new key (with optional name comment),
then re-encrypts the entire store to the updated recipient list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 12:50:00 -07:00
Brian Majewski
a10f390519 feat: shared _reencrypt_all + reencrypt cmd + dual rekey (EGB-283)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 12:44:44 -07:00
Brian Majewski
649aa78063 feat: secrets recipients list (EGB-283) 2026-06-24 12:39:52 -07:00
Brian Majewski
0f9de1c2fd feat: multi-recipient encrypt core + recipients.txt (EGB-283)
- Add RECIPIENTS_FILE_NAME / RECIPIENTS_FILE constants; update resolve_store to re-derive RECIPIENTS_FILE after store resolution.
- Add _validate_age_recipient (native age1 X25519 key format check, injection rail).
- Add RECIPIENT_ARGS global array and _load_recipients (absent → single pubkey legacy path; present → parse+validate recipients.txt, refuse symlink, die on bad/empty).
- Rewire all 5 push encrypt sites (push_dir_to_project, cmd_push inline, push_external_files ×2, cmd_push_workspaces) to use RECIPIENT_ARGS; drop pubkey threading from push_dir_to_project and push_external_files signatures.
- New test/recipients.bats (4 tests): legacy single-key, multi-recipient decrypt, invalid key rejection, symlink rejection.
- Fix test/test_helper.bash: set GIT_AUTHOR/COMMITTER env vars so git commit works with isolated $HOME.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 12:33:05 -07:00