fix(set): error on encryption failure; use LocalStack for AWS tests#324
fix(set): error on encryption failure; use LocalStack for AWS tests#324
Conversation
Add `endpoint` optional field to AWS KMS, Secrets Manager, and Parameter Store providers to support custom endpoints (like LocalStack). Convert all AWS test files to use LocalStack instead of real AWS credentials, removing the need for live AWS access in CI. Expand LocalStack services in CI from `sts,iam` to `sts,iam,kms,secretsmanager,ssm` and set up test resources (KMS key, secrets) during CI setup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello, 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 significantly enhances the development and testing workflow for AWS integrations by transitioning all related tests to use LocalStack. This change allows developers to run AWS-dependent tests locally and in CI without requiring actual AWS credentials, leading to faster feedback cycles and reduced operational costs. The introduction of an optional Highlights
Changelog
Ignored Files
Activity
Using Gemini Code AssistThe 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
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 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. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request refactors the AWS provider tests to use LocalStack, aiming to make the CI faster and more reliable. While the changes to add the endpoint field to AWS providers are well-implemented for testing, the unvalidated endpoint field introduces a potential credential theft vulnerability if a user is tricked into using a malicious configuration. It is recommended to add validation to the endpoint URL to ensure it uses secure schemes or is restricted to local addresses. Additionally, there is a suggestion to discuss a removed test case.
I am having trouble creating individual review comments. Click here to see my feedback.
src/providers/aws_kms.rs (145-147)
The endpoint field allows users to specify a custom URL for AWS KMS requests. This URL is used directly by the AWS SDK without validation. If a user is tricked into using a malicious configuration file (e.g., in a shared repository), an attacker could point the endpoint to a server they control. When fnox attempts to communicate with AWS, it will send the user's AWS credentials (e.g., in the Authorization header) to the attacker's server, leading to potential credential theft.
Remediation: Validate the endpoint URL. Restrict it to https:// schemes unless the host is localhost or 127.0.0.1. Alternatively, consider only allowing the endpoint to be set via environment variables (like AWS_ENDPOINT_URL) rather than in the persistent configuration file, or provide a clear warning to the user when a custom endpoint is being used.
src/providers/aws_ps.rs (161-163)
The endpoint field allows users to specify a custom URL for AWS Parameter Store requests. This URL is used directly by the AWS SDK without validation. If a user is tricked into using a malicious configuration file (e.g., in a shared repository), an attacker could point the endpoint to a server they control. When fnox attempts to communicate with AWS, it will send the user's AWS credentials (e.g., in the Authorization header) to the attacker's server, leading to potential credential theft.
Remediation: Validate the endpoint URL. Restrict it to https:// schemes unless the host is localhost or 127.0.0.1. Alternatively, consider only allowing the endpoint to be set via environment variables (like AWS_ENDPOINT_URL) rather than in the persistent configuration file, or provide a clear warning to the user when a custom endpoint is being used.
src/providers/aws_sm.rs (190-192)
The endpoint field allows users to specify a custom URL for AWS Secrets Manager requests. This URL is used directly by the AWS SDK without validation. If a user is tricked into using a malicious configuration file (e.g., in a shared repository), an attacker could point the endpoint to a server they control. When fnox attempts to communicate with AWS, it will send the user's AWS credentials (e.g., in the Authorization header) to the attacker's server, leading to potential credential theft.
Remediation: Validate the endpoint URL. Restrict it to https:// schemes unless the host is localhost or 127.0.0.1. Alternatively, consider only allowing the endpoint to be set via environment variables (like AWS_ENDPOINT_URL) rather than in the persistent configuration file, or provide a clear warning to the user when a custom endpoint is being used.
test/aws_kms.bats (200-209)
This test case, which verifies the behavior of fnox set when KMS encryption fails, has been removed. Removing tests can risk future regressions. Could you please explain why this test was removed? If the behavior it tested (falling back to plaintext) is no longer correct, please consider adding a new test to verify the current expected behavior (e.g., failing with an error).
Greptile SummaryThis PR delivers two related improvements: it fixes a silent-plaintext security regression in Key changes:
All changes are focused and correct. The production code changes are minimal and well-scoped; the endpoint plumbing is symmetric across all three AWS providers. Test migration is thorough and consistent. Confidence Score: 5/5
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["fnox set KEY value --provider P"] --> B{"provider is<br/>encryption type?"}
B -- No --> C{"provider is<br/>remote storage?"}
C -- No --> D["Store plaintext in fnox.toml"]
C -- Yes --> E["provider.put_secret(key, value)"]
E -- error --> F["Propagate error ❌"]
E -- ok --> G["Store remote key ref in fnox.toml"]
B -- Yes --> H{"dry-run?"}
H -- Yes --> I["Store '<encrypted>' placeholder"]
H -- No --> J["provider.encrypt(value)"]
J -- error --> K["Hard fail — propagate error ❌<br/>(previously: warn + store plaintext)"]
J -- ok --> L["Store base64 ciphertext in fnox.toml"]
subgraph AWS Providers
M["AwsKmsProvider<br/>(encrypt)"] --> N{"endpoint set?"}
N -- Yes --> O["Build client with endpoint_url<br/>e.g. http://localhost:4566"]
N -- No --> P["Build client with default AWS endpoint"]
end
J --> M
Last reviewed commit: 1c4aeae |
- Add back KMS error-path test using nonexistent key alias - Add LocalStack health-check polling to Parameter Store setup_file() - Remove dummy AWS credentials from GITHUB_ENV (tests set their own) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, if an encryption provider (age, aws-kms, azure-kms, etc.) failed to encrypt a value, fnox would silently warn and store the secret as plaintext. This is a security issue — encryption failures should be hard errors, not silent fallbacks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| // Encrypt with the provider — fail on error rather than | ||
| // silently storing plaintext | ||
| let encrypted = provider.encrypt(value).await?; | ||
| (Some(encrypted), None) |
There was a problem hiding this comment.
This PR is scoped as a test-infrastructure refactor (refactor(aws): use LocalStack for all AWS tests), but this change in set.rs affects production behavior for all users, not just tests.
Previously, a failed KMS encryption call would warn and fall back to storing plaintext (lines 150-156 in the prior version). Now it propagates the error via await? (line 152).
This is the correct security behavior (silent plaintext fallback was a security hole), but it is a breaking change: any user with a transient KMS error or a misconfigured key will now get a hard failure from fnox set instead of only a warning. This warrants a semver-minor or semver-major bump and a changelog entry, not just an incidental commit in a test refactor PR.
|
bugbot run |
| @@ -126,6 +126,22 @@ jobs: | |||
| curl -sf http://localhost:8200/v1/sys/health && break | |||
| sleep 1 | |||
| done | |||
| # Setup LocalStack resources for AWS provider tests | |||
| export AWS_ACCESS_KEY_ID=test | |||
| export AWS_SECRET_ACCESS_KEY=test | |||
| export AWS_DEFAULT_REGION=us-east-1 | |||
| # Create KMS key for tests | |||
| LOCALSTACK_KMS_KEY_ID=$(aws --endpoint-url http://localhost:4566 kms create-key \ | |||
| --region us-east-1 --query 'KeyMetadata.KeyId' --output text) | |||
| aws --endpoint-url http://localhost:4566 kms create-alias \ | |||
| --alias-name alias/fnox-testing \ | |||
| --target-key-id "$LOCALSTACK_KMS_KEY_ID" \ | |||
| --region us-east-1 | |||
| # Create shared test secret in Secrets Manager | |||
| aws --endpoint-url http://localhost:4566 secretsmanager create-secret \ | |||
| --name "fnox/test-secret" \ | |||
| --secret-string "This is a test secret in AWS Secrets Manager!" \ | |||
| --region us-east-1 | |||
There was a problem hiding this comment.
LocalStack service health check doesn't verify per-service readiness
The existing health-check loop (lines 120–123) only validates that the LocalStack container returns HTTP 200 from /_localstack/health. However, LocalStack reports container health before individual services like kms, secretsmanager, and ssm have finished initializing — the JSON response body will have those services in "starting" state, not "available" or "running", immediately after the health endpoint first starts responding.
This means aws kms create-key (line 134) can be called while KMS is still starting up, causing the command to fail and the entire CI step to exit non-zero. The result is an intermittent CI failure on cold runners where LocalStack takes longer to start each service.
A more robust check would verify per-service availability:
# Wait for LocalStack services to be ready
for _i in $(seq 1 30); do
if curl -sf http://localhost:4566/_localstack/health | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
services = data.get('services', {})
required = ['kms', 'secretsmanager', 'ssm']
all_ok = all(services.get(s) in ('available', 'running') for s in required)
sys.exit(0 if all_ok else 1)
" 2>/dev/null; then
break
fi
sleep 2
doneWithout this, the kms create-key → create-alias → secretsmanager create-secret block will fail intermittently on CI runners, causing the LOCALSTACK_ENDPOINT env var to never be exported and all downstream AWS tests to be silently skipped rather than run.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| @@ -1,61 +1,72 @@ | |||
| #!/usr/bin/env bats | |||
There was a problem hiding this comment.
bats file_tags=expensive tag silently removed
The # bats file_tags=expensive line was removed from this file. Looking at .github/workflows/ci.yml:
BATS_FILTER_TAGS: ${{ (github.event_name != 'pull_request' || !startsWith(github.head_ref, 'release-plz')) && '!expensive' || '' }}The filter excludes expensive-tagged tests on release-plz PRs. By removing this tag, the Secrets Manager tests will now run on release-plz PRs where they previously were skipped. This may be intentional (LocalStack tests are much cheaper than real AWS), but it's a behaviour change worth calling out — if LocalStack isn't available in the release-plz environment, these tests will attempt to run and then skip (or fail) rather than being excluded at the tag level.
- Use plain provider in default_provider override tests (these test config layering, not encryption, so fake age recipients broke them) - Assert error output content in wrong-key tests to verify failures are caused by the expected provider error, not unrelated issues Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
### 🐛 Bug Fixes - **(set)** error on encryption failure; use LocalStack for AWS tests by [@jdx](https://github.com/jdx) in [#324](#324) ### 📦️ Dependency Updates - add Cross.toml to install libudev-dev for linux cross-compilation by [@jdx](https://github.com/jdx) in [#326](#326)
Summary
fnox setpreviously swallowed encryption errors from any encryption provider (age, aws-kms, azure-kms, gcp-kms, fido2, yubikey) and silently stored secrets as plaintext. Now encryption failures are hard errors.endpointoptional field to AWS KMS, Secrets Manager, and Parameter Store providers for custom endpoint support (e.g., LocalStack)sts,iamtosts,iam,kms,secretsmanager,ssmTest plan
🤖 Generated with Claude Code
Note
Medium Risk
Changes secret-writing semantics by failing instead of silently storing plaintext, which may break existing workflows that relied on the fallback. Adds new AWS endpoint configuration paths and rewires AWS tests/CI to LocalStack, which could surface environment-specific issues.
Overview
Behavior change:
fnox setno longer falls back to storing plaintext when an encryption provider fails; encryption errors now fail the command.AWS provider updates: Adds optional
endpointsupport toaws-kms,aws-sm, andaws-psprovider configs (schema + wizard metadata), and wires it through the AWS SDK clients so requests can target custom endpoints (e.g., LocalStack).Test/CI changes: CI now starts LocalStack with additional services and pre-creates KMS/Secrets Manager resources; AWS bats tests are rewritten to use
LOCALSTACK_ENDPOINTwith dummy creds and updated assertions (including expecting failures on wrong keys).Written by Cursor Bugbot for commit 1c4aeae. This will update automatically on new commits. Configure here.