diff --git a/README.md b/README.md index fc6c1c7..e79fc15 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ secrets clear | `secrets list` | Show all projects that have stored secrets | | `secrets rm ` | Delete a project's secrets from the store | | `secrets rekey` | Generate a new encryption key and re-encrypt everything | -| `secrets verify` | Check the current project's `.secrets.json` against the store (missing/orphaned blobs) and decrypt every blob | +| `secrets verify [project]` | Check the current project's `.secrets.json` against the store (missing/orphaned blobs) and decrypt every blob. `[project]` overrides the store directory name; the manifest is still read from the current directory | | `secrets verify --all` | Decrypt-test every blob in every project — a store-wide integrity sweep | ### Automatic project detection diff --git a/secrets b/secrets index 61ea874..5ed68a5 100755 --- a/secrets +++ b/secrets @@ -1980,6 +1980,9 @@ _verify_project() { if ! _validate_dotenv_rel_path "$rel" 2>/dev/null; then echo "FINDING: unsafe dotenv path in $SECRETS_JSON_NAME: '$rel' (will be refused)." >&2 findings=$((findings + 1)) + # Still account for it so a matching stray blob isn't ALSO flagged as an + # orphan (one bad entry → one finding, not two). + expected="$expected$rel.age"$'\n' continue fi expected="$expected$rel.age"$'\n' @@ -1997,8 +2000,9 @@ _verify_project() { done < <(jq -r '.dotenv // [] | .[]' "$manifest") # ── external entries: missing-blob + decrypt ── - local etype epath ekeys slug erel eblob - while IFS=$'\t' read -r etype epath ekeys; do + # verify only needs type + path to locate the blob; keys are irrelevant here. + local etype epath slug erel eblob + while IFS=$'\t' read -r etype epath _; do [ -n "$etype" ] || continue slug=$(_secrets_files_slug "$epath") erel="external/$slug.$etype.age" diff --git a/test/manifest.bats b/test/manifest.bats index 3719e8e..9df5bfa 100644 --- a/test/manifest.bats +++ b/test/manifest.bats @@ -754,6 +754,9 @@ m_nojq_path() { "$SECRETS_BIN" push >/dev/null 2>&1 run "$SECRETS_BIN" verify [ "$status" -eq 0 ] + # Pin the count so a silent under-count (e.g. skipping the nested or external + # blob while still exiting 0) is caught: 3 dotenv + 1 external = 4. + [[ "$output" == *"4 blob(s) verified"* ]] || false } @test "verify: declared-but-missing blob is a finding (exit 1)" { @@ -870,7 +873,8 @@ m_nojq_path() { printf 'garbage' > "$SECRETS_DIR/verifyextcorrupt/external/"*.age run "$SECRETS_BIN" verify [ "$status" -eq 1 ] - [[ "$output" == *"external"* ]] || false + # Pin the decrypt-fail branch specifically, not just any external finding. + [[ "$output" == *"does not decrypt"* ]] || false } @test "verify: an unsafe dotenv path in the manifest is a finding (exit 1)" {