Skip to content

fix(sync): skip post-processing when resolving secrets for sync#371

Merged
jdx merged 2 commits intojdx:mainfrom
rpendleton:rpendleton/fix-syncing-with-json-paths
Apr 4, 2026
Merged

fix(sync): skip post-processing when resolving secrets for sync#371
jdx merged 2 commits intojdx:mainfrom
rpendleton:rpendleton/fix-syncing-with-json-paths

Conversation

@rpendleton
Copy link
Copy Markdown
Contributor

Summary

When syncing secrets that use json_path, resolve_secrets_batch was applying json_path extraction before storing the result, caching the extracted value instead of the full raw secret. When reading the secret after that sync, json_path would be applied again to the already-extracted value, failing with "Failed to parse JSON secret".

Fix

Add SecretConfig::for_raw_resolve() which strips post-processing fields (json_path, sync, default) so callers that need raw provider values have a single, consistent method to use. Both sync and reencrypt now use it, ensuring new post-processing steps only need updating in one place.

Test plan

  • cargo test — unit tests for for_raw_resolve()
  • mise run test:bats -- test/sync.bats — added tests for syncing secrets with json_path
  • mise run test:bats -- test/reencrypt.bats — added tests for reencrypt (with and without json_path)

🤖 Generated with Claude Code

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 24, 2026

Greptile Summary

This PR fixes a bug where fnox sync was incorrectly caching post-processed (json_path-extracted) values instead of the raw provider value. On subsequent reads, json_path would be applied a second time to the already-extracted string, failing with "Failed to parse JSON secret".

The fix introduces SecretConfig::for_raw_resolve() — a single, documented method that strips json_path, sync, and default before a raw-provider resolution. Both sync and reencrypt now delegate to this helper, so any future post-processing fields only need updating in one place.

  • src/config.rs: New for_raw_resolve() method with three unit tests covering stripping, preservation of non-post-processing fields, and the no-op case.
  • src/commands/sync.rs: Root bug fix — now calls for_raw_resolve() so json_path (and default) are stripped before values are cached in the sync blob.
  • src/commands/reencrypt.rs: Existing manual field-stripping replaced with for_raw_resolve() — behaviour is identical, code is simpler.
  • test/sync.bats: Integration tests for json_path syncing and skipping default-only secrets.
  • test/reencrypt.bats: Integration test for reencrypting secrets that use json_path.

Confidence Score: 5/5

PR is safe to merge — fixes a real double-extraction bug with correct logic and comprehensive tests

The change is minimal, well-contained, and directly addresses the described bug. The new for_raw_resolve() abstraction is clean and all three unit tests plus new integration tests validate correct behaviour. No P0/P1 issues found.

No files require special attention

Important Files Changed

Filename Overview
src/config.rs Adds for_raw_resolve() helper that strips json_path, sync, and default fields; backed by three unit tests
src/commands/sync.rs Fixes root bug: uses for_raw_resolve() so raw JSON blobs (not extracted values) are cached during sync
src/commands/reencrypt.rs Simplifies existing manual field-stripping to a single for_raw_resolve() call; behaviour is unchanged
test/sync.bats Adds integration tests for json_path syncing and default-only secret skipping
test/reencrypt.bats Adds integration test for reencrypting secrets that use json_path

Sequence Diagram

sequenceDiagram
    participant User
    participant SyncCmd as sync command
    participant ForRaw as for_raw_resolve()
    participant Resolver as resolve_secrets_batch
    participant Provider as Source Provider
    participant Age as Age Provider (target)

    User->>SyncCmd: fnox sync -p age
    SyncCmd->>ForRaw: sc.for_raw_resolve()
    Note over ForRaw: strips json_path, sync, default
    ForRaw-->>SyncCmd: raw SecretConfig
    SyncCmd->>Resolver: resolve_secrets_batch(raw configs)
    Resolver->>Provider: fetch raw value (e.g. full JSON blob)
    Provider-->>Resolver: '{"username":"admin",...}'
    Resolver-->>SyncCmd: raw plaintext map
    SyncCmd->>Age: encrypt(raw plaintext)
    Age-->>SyncCmd: encrypted blob
    SyncCmd->>SyncCmd: store sync = { provider, encrypted_blob }
    Note over SyncCmd: json_path preserved in config,\napplied at read time only
Loading

Greploops — Automatically fix all review issues by running /greploops in Claude Code. It iterates: fix, push, re-review, repeat until 5/5 confidence.
Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal.

Reviews (3): Last reviewed commit: "Merge branch 'main' into rpendleton/fix-..." | Re-trigger Greptile

Comment thread test/reencrypt.bats Outdated
Comment thread test/reencrypt.bats Outdated
When syncing secrets that use json_path, resolve_secrets_batch was
applying json_path extraction before storing the result, caching the
extracted value instead of the full raw secret. When reading the
secret after that sync, json_path would be applied again to the
already-extracted value, failing with "Failed to parse JSON secret".

Add SecretConfig::for_raw_resolve() which strips post-processing fields
(json_path, sync, default) so callers that need raw provider values
have a single, consistent method to use. Both sync and reencrypt now
use it, ensuring new post-processing steps only need updating in one
place.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rpendleton rpendleton force-pushed the rpendleton/fix-syncing-with-json-paths branch from f51591c to b04f098 Compare March 24, 2026 05:50
@rpendleton
Copy link
Copy Markdown
Contributor Author

rpendleton commented Mar 24, 2026

The LocalStack CI failure is likely unrelated and due to changes described in this announcement.

LocalStack is consolidating its community and pro editions into a single unified image, and moving forward, newer versions of the localstack/localstack image require an auth token to run. You'll either need to sign up for a LocalStack account and configure a CI auth token, or pin the image to an earlier version (such as 4.14.0) to stay on the last community release before this change was made.

@jdx jdx enabled auto-merge (squash) April 4, 2026 13:09
@jdx jdx merged commit f788890 into jdx:main Apr 4, 2026
14 checks passed
jdx pushed a commit that referenced this pull request Apr 4, 2026
### 🚀 Features

- **(provider)** add Doppler secrets manager provider by
[@natefaerber](https://github.com/natefaerber) in
[#376](#376)

### 🐛 Bug Fixes

- **(ci)** pin LocalStack to v4 (last free community version) by
[@jdx](https://github.com/jdx) in
[#379](#379)
- **(sync)** skip post-processing when resolving secrets for sync by
[@rpendleton](https://github.com/rpendleton) in
[#371](#371)

### 🚜 Refactor

- **(providers)** extract shared error helpers to FnoxError methods by
[@jdx](https://github.com/jdx) in
[#380](#380)

### 📦️ Dependency Updates

- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#369](#369)
- update taiki-e/upload-rust-binary-action digest to 0e34102 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#372](#372)
- update dependency vue to v3.5.32 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#373](#373)

### New Contributors

- @natefaerber made their first contribution in
[#376](#376)
- @rpendleton made their first contribution in
[#371](#371)
@rpendleton rpendleton deleted the rpendleton/fix-syncing-with-json-paths branch April 26, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants