Skip to content

fix(set): prompt for secret value when -k flag is used#367

Merged
jdx merged 4 commits intomainfrom
fix/set-key-name-prompt
Mar 22, 2026
Merged

fix(set): prompt for secret value when -k flag is used#367
jdx merged 4 commits intomainfrom
fix/set-key-name-prompt

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Mar 22, 2026

Summary

  • Removed key_name from the has_metadata check in set.rs-k specifies the provider key name, not a metadata-only operation, so users should still be prompted for the secret value
  • Removed code that wrote key_name directly as the config value, bypassing provider storage
  • Added bats tests verifying -k correctly reads secrets from stdin and explicit arguments

Fixes #366

Test plan

  • mise run test:bats -- test/set_key_name.bats passes
  • Verify fnox set -g -p provider -k keyname SECRET prompts for value interactively
  • Verify echo "val" | fnox set -p provider -k keyname SECRET reads from stdin

🤖 Generated with Claude Code


Note

Medium Risk
Changes fnox set secret-value acquisition and persistence logic when -k/--key-name is present, which can affect whether secrets are prompted/read and what ultimately gets stored. While scoped, mistakes here could lead to missing secrets or incorrect config values.

Overview
Fixes fnox set so -k/--key-name no longer triggers the metadata-only path: when -k is used without an explicit value, the command will still read from stdin or prompt interactively for the secret value.

Removes the behavior that wrote key_name directly into the secret value field, ensuring provider/encryption handling determines what is persisted instead. Adds Bats coverage (test/set_key_name.bats) to verify stdin and explicit-value flows with -k (including when combined with -d) don’t store plaintext or the key name for encryption providers.

Written by Cursor Bugbot for commit 1f5d4f6. This will update automatically on new commits. Configure here.

jdx and others added 2 commits March 22, 2026 19:07
When using `fnox set -k`, the command skipped the secret value prompt
because key_name was incorrectly included in the has_metadata check.
This caused the secret to never be written to the provider while the
config was updated as if it succeeded.

Fixes #366

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify that `fnox set -k` correctly reads secret values from stdin
and from explicit arguments, rather than silently skipping the prompt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where using the -k flag (specifying a provider key name) inadvertently prevented the system from prompting for the actual secret value. The changes ensure that the set command consistently prompts for or accepts a secret value, even when a key_name is provided, thereby maintaining expected security and data handling practices. It also includes new tests to verify this corrected behavior.

Highlights

  • Secret Value Prompting: The key_name flag (-k) no longer bypasses the secret value prompt, ensuring that users are always prompted for the secret value when using this flag.
  • Configuration Logic Refinement: Removed logic that incorrectly wrote the key_name directly as the secret's configuration value, ensuring proper provider storage and encryption.
  • New Test Coverage: Added new Bats tests to validate that the set -k command correctly reads secret values from both standard input and explicit command-line arguments.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses a bug where using the -k flag with fnox set would incorrectly prevent prompting for a secret value. The changes in src/commands/set.rs correctly adjust the logic to ensure a value is handled when -k is used. Additionally, new tests in test/set_key_name.bats verify the fix. My review includes a suggestion to refactor the new test file to reduce code duplication, thereby improving its maintainability.

Comment thread test/set_key_name.bats
Comment on lines +3 to +68
setup() {
load 'test_helper/common_setup'
_common_setup
}

teardown() {
_common_teardown
}

@test "fnox set -k prompts for secret value (reads from stdin)" {
# Generate age key
if ! command -v age-keygen >/dev/null 2>&1; then
skip "age-keygen not installed"
fi

local keygen_output
keygen_output=$(age-keygen -o key.txt 2>&1)
local public_key
public_key=$(echo "$keygen_output" | grep "^Public key:" | cut -d' ' -f3)

cat >test-config.toml <<EOF
[providers.age]
type = "age"
recipients = ["$public_key"]

[secrets]
EOF

# Pipe secret value via stdin with -k flag — should encrypt and store
run bash -c 'echo "my-secret-value" | "$FNOX_BIN" --config test-config.toml set -p age -k custom-key-name MY_SECRET'
assert_success

# The config should reference the secret
assert_file_contains test-config.toml "MY_SECRET"
# The plaintext secret value should NOT appear in the config (it should be encrypted)
assert_file_not_contains test-config.toml "my-secret-value"
}

@test "fnox set -k with explicit value stores the secret" {
# Generate age key
if ! command -v age-keygen >/dev/null 2>&1; then
skip "age-keygen not installed"
fi

local keygen_output
keygen_output=$(age-keygen -o key.txt 2>&1)
local public_key
public_key=$(echo "$keygen_output" | grep "^Public key:" | cut -d' ' -f3)

cat >test-config.toml <<EOF
[providers.age]
type = "age"
recipients = ["$public_key"]

[secrets]
EOF

# Provide value as argument with -k flag
run "$FNOX_BIN" --config test-config.toml set -p age -k custom-key-name MY_SECRET "my-secret-value"
assert_success

# The config should reference the secret
assert_file_contains test-config.toml "MY_SECRET"
# The plaintext secret value should NOT appear (it should be encrypted)
assert_file_not_contains test-config.toml "my-secret-value"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The two tests in this file share a significant amount of setup code for generating an age key and creating a test-config.toml file. To improve maintainability and reduce redundancy, this common logic can be extracted into the setup function, which runs before each test. This will make the tests cleaner and easier to read.

setup() {
	load 'test_helper/common_setup'
	_common_setup

	# Generate age key and config for tests
	if ! command -v age-keygen >/dev/null 2>&1; then
		skip "age-keygen not installed"
	fi

	local keygen_output
	keygen_output=$(age-keygen -o key.txt 2>&1)
	local public_key
	public_key=$(echo "$keygen_output" | grep "^Public key:" | cut -d' ' -f3)

	cat >test-config.toml <<EOF
[providers.age]
type = "age"
recipients = ["$public_key"]

[secrets]
EOF
}

teardown() {
	_common_teardown
}

@test "fnox set -k prompts for secret value (reads from stdin)" {
	# Pipe secret value via stdin with -k flag — should encrypt and store
	run bash -c 'echo "my-secret-value" | "$FNOX_BIN" --config test-config.toml set -p age -k custom-key-name MY_SECRET'
	assert_success

	# The config should reference the secret
	assert_file_contains test-config.toml "MY_SECRET"
	# The plaintext secret value should NOT appear in the config (it should be encrypted)
	assert_file_not_contains test-config.toml "my-secret-value"
}

@test "fnox set -k with explicit value stores the secret" {
	# Provide value as argument with -k flag
	run "$FNOX_BIN" --config test-config.toml set -p age -k custom-key-name MY_SECRET "my-secret-value"
	assert_success

	# The config should reference the secret
	assert_file_contains test-config.toml "MY_SECRET"
	# The plaintext secret value should NOT appear (it should be encrypted)
	assert_file_not_contains test-config.toml "my-secret-value"
}

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 22, 2026

Greptile Summary

This PR fixes a bug where passing -k/--key-name to fnox set caused the command to silently skip reading the secret value and instead write the key name string directly as the stored config value — bypassing encryption and remote-storage providers entirely.

Key changes:

  • src/commands/set.rs: Removes key_name from the has_metadata guard and refines the metadata-only skip condition to has_metadata && self.key_name.is_none(). When -k is present (with or without other metadata flags), the command now falls through to stdin-read or interactive prompt as intended. The stale block that wrote self.key_name directly via set_value is also removed, ensuring secrets always flow through provider logic.
  • test/set_key_name.bats: Adds three bats tests covering stdin piping, explicit value argument, and -k combined with -d, all verifying that plaintext never leaks into the config and that the encrypted blob is stored instead.

Confidence Score: 4/5

  • Safe to merge; the logic fix is correct, no regressions introduced, and tests cover the patched paths.
  • The two-line logic change is minimal and provably correct: removing key_name from has_metadata and adding && self.key_name.is_none() to the skip guard covers every combination of flags without any side-effects on existing metadata-only or plain-value flows. The buggy direct set_value(key_name) write is cleanly excised. Three new bats tests exercise stdin, explicit argument, and the combined -k+-d path. The only remaining gap (no test for the remote-storage provider path where key_name is actually consumed) was already noted in a prior review thread and is a follow-up item, not a blocker.
  • No files require special attention.

Important Files Changed

Filename Overview
src/commands/set.rs Two focused changes: (1) removes key_name from has_metadata and guards the metadata-only skip with && self.key_name.is_none(), ensuring -k always triggers value capture; (2) removes the stale block that wrote key_name directly as the stored config value, so all values now flow through the provider encryption/remote-storage path. Logic is correct and no regressions introduced.
test/set_key_name.bats Three new bats tests cover: stdin piping with -k, explicit value argument with -k, and -k combined with -d. All use an age (encryption) provider, which means the remote-storage path for key_name is never exercised. Minor gap: the third test is missing assert_file_not_contains test-config.toml "custom-key-name", unlike the first two tests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[fnox set KEY] --> B{value arg provided?}
    B -- yes --> C[secret_value = arg]
    B -- no --> D{has_metadata AND key_name is None?}
    D -- yes --> E[secret_value = None\nmetadata-only update]
    D -- no --> F{stdin piped?}
    F -- yes --> G[read from stdin]
    F -- no --> H[interactive prompt]
    G --> I[secret_value = stdin]
    H --> I

    C --> J{provider set?}
    I --> J
    J -- encryption provider --> K[encrypt value]
    J -- remote storage provider --> L[put_secret with key_name or KEY\nreturns stored key]
    J -- none/other --> M[store plaintext]
    K --> N[set_value encrypted blob]
    L --> O[set_value remote key ref]
    M --> P[set_value plaintext]
    E --> Q[update description/if_missing/default only]
Loading

Reviews (3): Last reviewed commit: "fix(set): don't skip value prompt when -..." | Re-trigger Greptile

Comment thread test/set_key_name.bats
Move age key generation into setup() to reduce duplication. Add
negative assertions verifying key_name is not written to config
for encryption providers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/commands/set.rs
When -k was combined with metadata flags like -d without an explicit
value, has_metadata was true so the value prompt was skipped, silently
discarding the -k flag. Now -k prevents the metadata-only skip path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jdx jdx enabled auto-merge (squash) March 22, 2026 21:54
@jdx jdx merged commit 7bdd094 into main Mar 22, 2026
16 checks passed
@jdx jdx deleted the fix/set-key-name-prompt branch March 22, 2026 21:57
jdx pushed a commit that referenced this pull request Mar 22, 2026
### 🚀 Features

- add `reencrypt` subcommand for updating encryption recipients by
[@jdx](https://github.com/jdx) in
[#365](#365)

### 🐛 Bug Fixes

- **(set)** prompt for secret value when -k flag is used by
[@jdx](https://github.com/jdx) in
[#367](#367)

### 📦️ Dependency Updates

- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#360](#360)
- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#362](#362)
fullerzz pushed a commit to fullerzz/fnox-py that referenced this pull request Mar 23, 2026
## Upstream release

Bumps bundled fnox binary from 1.18.0 to 1.19.0.

**Release**: https://github.com/jdx/fnox/releases/tag/v1.19.0

## Release notes

v1.19.0 adds a new `fnox reencrypt` command that makes it easy to
re-encrypt all your secrets when encryption provider configuration
changes -- for example, when adding or removing age recipients. This
release also fixes a bug where `fnox set -k` would skip prompting for
the secret value.

## Added

**`fnox reencrypt` subcommand**
([#365](jdx/fnox#365)) -- @jdx

Previously, re-encrypting secrets after changing recipients required a
tedious manual loop of `fnox get` and `fnox set` for each secret. The
new `fnox reencrypt` command handles this in one step: it decrypts
matching secrets and re-encrypts them with the current provider
configuration, writing the updated ciphertext back to the correct source
config file.

```bash
# Re-encrypt all age secrets
fnox reencrypt -p age

# Preview what would be re-encrypted
fnox reencrypt -p age --dry-run

# Re-encrypt specific keys
fnox reencrypt MY_SECRET OTHER_SECRET

# Filter by regex pattern
fnox reencrypt --filter "^DB_"

# Skip the confirmation prompt
fnox reencrypt -p age -f
```

The command correctly handles multi-line secrets, writes back to the
original source file (including distinguishing root `[secrets]` from
`[profiles.X.secrets]`), clears stale sync caches, and scrubs decrypted
plaintext from the process environment after re-encryption. Only secrets
backed by encryption-capable providers are eligible.

## Fixed

**`fnox set -k` now correctly prompts for the secret value**
([#367](jdx/fnox#367)) -- @jdx

When using the `-k` / `--key-name` flag with `fnox set`, the command
incorrectly treated it as a metadata-only operation and skipped
prompting for the secret value. It also wrote the key name itself as the
stored config value, bypassing the encryption provider entirely. Now
`-k` works as expected: the secret value is read from stdin, a
command-line argument, or an interactive prompt, and is properly
encrypted and stored.

```bash
# These now work correctly
echo "my-secret" | fnox set -p age -k custom-key-name MY_SECRET
fnox set -p age -k custom-key-name MY_SECRET "my-secret"
```

**Full Changelog**:
jdx/fnox@v1.18.0...v1.19.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant