docs: plan fixups from Task 1 review (blob path, dangling-symlink fix to Task 8)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0f9de1c2fd
commit
14cf86fba9
1 changed files with 41 additions and 11 deletions
|
|
@ -62,7 +62,7 @@ make_second_identity() {
|
|||
# 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/work/myproj/.env.age"
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
@ -75,10 +75,10 @@ make_second_identity() {
|
|||
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/work/myproj/.env.age"
|
||||
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/work/myproj/.env.age"
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
@ -361,18 +361,18 @@ git commit -m "feat: secrets recipients list (EGB-283)"
|
|||
@test "reencrypt re-encrypts existing blobs to a newly added recipient line" {
|
||||
init_with_remote
|
||||
create_project_dir myproj
|
||||
run "$SECRETS_BIN" push # single-key blob
|
||||
run "$SECRETS_BIN" push # single-key blob (project name = "myproj"; blob at $SECRETS_DIR/myproj/.env.age)
|
||||
[ "$status" -eq 0 ]
|
||||
make_second_identity
|
||||
STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt")
|
||||
printf '%s\n%s\n' "$STORE_PUB" "$BOB_PUB" > "$SECRETS_DIR/recipients.txt"
|
||||
# Bob cannot read the old single-key blob yet.
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/work/myproj/.env.age"
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -ne 0 ]
|
||||
run "$SECRETS_BIN" reencrypt
|
||||
[ "$status" -eq 0 ]
|
||||
# Now he can.
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/work/myproj/.env.age"
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
@ -389,7 +389,7 @@ git commit -m "feat: secrets recipients list (EGB-283)"
|
|||
# No new keypair was generated.
|
||||
[ "$(cat "$SECRETS_DIR/key.txt")" = "$before" ]
|
||||
# Both recipients can decrypt.
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/work/myproj/.env.age"
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
@ -401,7 +401,7 @@ git commit -m "feat: secrets recipients list (EGB-283)"
|
|||
run "$SECRETS_BIN" rekey
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(cat "$SECRETS_DIR/key.txt")" != "$before" ]
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/work/myproj/.env.age"
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
```
|
||||
|
|
@ -562,7 +562,7 @@ git commit -m "feat: shared _reencrypt_all + reencrypt cmd + dual rekey (EGB-283
|
|||
[[ "$output" == *"recipients: 2"* ]] || false
|
||||
[[ "$output" == *"bob"* ]] || false
|
||||
# Existing blob re-encrypted: bob can read it.
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/work/myproj/.env.age"
|
||||
run age -d -i "$TEST_TMPDIR/bob.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
@ -696,7 +696,7 @@ git commit -m "feat: secrets recipients add (EGB-283)"
|
|||
run "$SECRETS_BIN" recipients list
|
||||
[[ "$output" == *"recipients: 1"* ]] || false
|
||||
# Store key still reads its own blobs.
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/work/myproj/.env.age"
|
||||
run age -d -i "$SECRETS_DIR/key.txt" "$SECRETS_DIR/myproj/.env.age"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
@ -1023,12 +1023,42 @@ git commit -m "feat: verify asserts blob recipient-count matches recipients.txt
|
|||
### Task 8: Security regression fixtures for `recipients.txt`
|
||||
|
||||
**Files:**
|
||||
- Modify: `test/recipients.bats` (adversarial fixtures only — no `secrets` changes expected; if a case slips through, fix the rail in `secrets` here)
|
||||
- Modify: `secrets` (one ordering fix in `_load_recipients`, carried over from the Task 1 review); `test/recipients.bats` (adversarial fixtures; if any other case slips through, fix the rail in `secrets` here)
|
||||
|
||||
**Interfaces:** none new — exercises `_load_recipients` / `_recipients_add` rails via the CLI.
|
||||
|
||||
> Policy reminder: these are ordinary defensive regression tests. Do NOT dispatch red-team/adversarial-review subagents and do NOT run `./test/run-security.sh` — that is operator-local.
|
||||
|
||||
- [ ] **Step 0: Fix the dangling-symlink ordering gap (from Task 1 review)**
|
||||
|
||||
In `_load_recipients`, the `[ ! -e "$RECIPIENTS_FILE" ]` check runs BEFORE the `[ -L "$RECIPIENTS_FILE" ]` check. A *dangling* symlink (link exists, target missing) makes `[ ! -e ]` true, so it bypasses the symlink refusal and silently degrades to legacy single-key mode. Swap the order so the symlink check comes first:
|
||||
|
||||
```bash
|
||||
_load_recipients() {
|
||||
RECIPIENT_ARGS=()
|
||||
if [ -L "$RECIPIENTS_FILE" ]; then
|
||||
die "Refusing to read symlinked $RECIPIENTS_FILE_NAME (security)."
|
||||
fi
|
||||
if [ ! -e "$RECIPIENTS_FILE" ]; then
|
||||
RECIPIENT_ARGS=(-r "$(get_pubkey)")
|
||||
return 0
|
||||
fi
|
||||
# … rest unchanged (line-by-line parse + validate) …
|
||||
```
|
||||
|
||||
Add a regression test alongside the other SECURITY fixtures:
|
||||
|
||||
```bash
|
||||
@test "SECURITY: a dangling symlink recipients.txt is refused, not silently ignored" {
|
||||
init_with_remote
|
||||
ln -s "$TEST_TMPDIR/does-not-exist.txt" "$SECRETS_DIR/recipients.txt"
|
||||
create_project_dir myproj
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"symlink"* ]] || false
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 1: Write the fixtures**
|
||||
|
||||
```bash
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue