fix(rotate-secret): verify --save-to-bitwarden, and store the secret rather than the description #114

Merged
jfraeys merged 1 commit from jfraeys/fix/rotate-secret-bitwarden into main 2026-10-13 14:30:58 -04:00
Owner

Found while checking --save-to-bitwarden after a rotate-secret run:

  • save_to_bitwarden built the item with '.notes = $content | .notes = $notes': the second assignment overwrote the first, so Bitwarden got only the description and never the rotated secret, while the script logged 'Saved ... to Bitwarden'.
  • bw create/edit output and status were discarded; success was unconditional, as was 'Bitwarden backup processed'.
  • Only vaultwarden and lldap had a case; other services silently saved nothing.
  • The closing hint printed a bare 'ansible-playbook playbooks/rotate-secret.yml', which the playbook's guard refuses after the SOPS cutover (needs scripts/with-vault.sh).

Now: scripts/lib/bitwarden.sh bw_store_verified puts the secret in the note body and the description in a custom field, syncs, reads the item back and compares (never printing the value), and returns non-zero on any failed step. rotate-secret.sh reports failure explicitly and says when a service has no Bitwarden item. The hint uses RUN_PREFIX.

Tests: scripts/tests/test-bitwarden-save.sh, 8 cases against a stateful fake bw (create, in-place update, failed create, failed sync, wrong stored value, secret never printed); reintroducing the overwrite bug fails 3 of them. Wired into lint. Not run against real Bitwarden.

Note: items already saved by the old code hold the description, not the secret; re-run --save-to-bitwarden after merging.

🤖 Generated with Claude Code

Found while checking --save-to-bitwarden after a rotate-secret run: - save_to_bitwarden built the item with '.notes = $content | .notes = $notes': the second assignment overwrote the first, so Bitwarden got only the description and never the rotated secret, while the script logged 'Saved ... to Bitwarden'. - bw create/edit output and status were discarded; success was unconditional, as was 'Bitwarden backup processed'. - Only vaultwarden and lldap had a case; other services silently saved nothing. - The closing hint printed a bare 'ansible-playbook playbooks/rotate-secret.yml', which the playbook's guard refuses after the SOPS cutover (needs scripts/with-vault.sh). Now: scripts/lib/bitwarden.sh bw_store_verified puts the secret in the note body and the description in a custom field, syncs, reads the item back and compares (never printing the value), and returns non-zero on any failed step. rotate-secret.sh reports failure explicitly and says when a service has no Bitwarden item. The hint uses RUN_PREFIX. Tests: scripts/tests/test-bitwarden-save.sh, 8 cases against a stateful fake bw (create, in-place update, failed create, failed sync, wrong stored value, secret never printed); reintroducing the overwrite bug fails 3 of them. Wired into lint. Not run against real Bitwarden. Note: items already saved by the old code hold the description, not the secret; re-run --save-to-bitwarden after merging. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(rotate-secret): --save-to-bitwarden stored the description instead of the secret, never verified, and the playbook hint skipped with-vault.sh
All checks were successful
Lint / lint (pull_request) Successful in 1m55s
Secret scan / scan (pull_request) Successful in 15s
b5936be2bb
jq chained '.notes = $content | .notes = $notes', so the secret was overwritten; bw create/edit results were discarded
and success was always logged. bw_store_verified (scripts/lib/bitwarden.sh) stores the secret in the note, syncs, reads it
back and compares, failing loudly otherwise. Services without a Bitwarden item now say so. The replay hint carries the
with-vault.sh prefix the playbook's guard requires.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jfraeys/infra!114
No description provided.