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>
This commit is contained in:
parent
3a7eea5529
commit
0f9de1c2fd
3 changed files with 129 additions and 14 deletions
57
test/recipients.bats
Normal file
57
test/recipients.bats
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env bats
|
||||
load test_helper
|
||||
|
||||
# A throwaway second identity for "another teammate".
|
||||
make_second_identity() {
|
||||
age-keygen -o "$TEST_TMPDIR/bob.txt" 2>/dev/null
|
||||
BOB_PUB=$(age-keygen -y "$TEST_TMPDIR/bob.txt")
|
||||
}
|
||||
|
||||
@test "push without recipients.txt stays single-key (legacy behavior)" {
|
||||
init_with_remote
|
||||
create_project_dir myproj
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
# No recipients.txt was created by push.
|
||||
[ ! -e "$SECRETS_DIR/recipients.txt" ]
|
||||
# Blob decrypts with the store's own key.
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "push with a hand-written recipients.txt encrypts to every listed key" {
|
||||
init_with_remote
|
||||
make_second_identity
|
||||
STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt")
|
||||
printf '# self\n%s\n# bob\n%s\n' "$STORE_PUB" "$BOB_PUB" > "$SECRETS_DIR/recipients.txt"
|
||||
create_project_dir myproj
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
# Bob (a recipient) can decrypt the pushed blob with HIS key.
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
# And the store key still can too.
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "push refuses a recipients.txt with an invalid key" {
|
||||
init_with_remote
|
||||
STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt")
|
||||
printf '%s\nnot-an-age-key\n' "$STORE_PUB" > "$SECRETS_DIR/recipients.txt"
|
||||
create_project_dir myproj
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"Invalid recipient"* ]] || false
|
||||
}
|
||||
|
||||
@test "push refuses a symlinked recipients.txt" {
|
||||
init_with_remote
|
||||
STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt")
|
||||
printf '%s\n' "$STORE_PUB" > "$TEST_TMPDIR/elsewhere.txt"
|
||||
ln -s "$TEST_TMPDIR/elsewhere.txt" "$SECRETS_DIR/recipients.txt"
|
||||
create_project_dir myproj
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"symlink"* ]] || false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue