ADDED: - encryption_bridge.rs: Service integration layer - encryption_contract_parser.rs: Nickel contract parsing - encryption_integration.rs: Integration tests (+442 lines) - docs/ENCRYPTION-*.md: Quick start, setup, architecture - examples/08-encryption: Usage examples - scripts/encryption-test-setup.sh: Provisioning MODIFIED: - helpers.rs: +570 lines utility functions - nickel/: Enhanced contract parsing & serialization - form_parser.rs: Constraint interpolation improvements - config/mod.rs: New configuration (+24 lines) - typedialog/src/main.rs: CLI updates (+83 lines) - Cargo.toml: encryption_bridge dependency - Cargo.lock, SBOMs: Updated AFFECTED BACKENDS: cli, tui, web (core-level changes)
11 KiB
How to Test SOPS Integration with typedialog
Complete step-by-step guide to testing SOPS encryption backend with typedialog.
Overview
This guide shows how to:
- Setup SOPS with Age (for local testing)
- Test SOPS encryption manually
- Integrate SOPS with typedialog
- Verify encryption/decryption works
Prerequisites
# Install tools
brew install sops age-keygen
# Verify installation
sops --version
age-keygen --version
# Check typedialog is available
which typedialog
typedialog --version
Test 1: Setup SOPS Configuration
1.1 Generate Age Key
# Generate a key for SOPS to use
age-keygen -o ~/.age/sops-test-key.txt
# View the key
cat ~/.age/sops-test-key.txt
# Output:
# # created: 2025-12-21T12:34:56Z
# # public key: age1xxxxxxxxxxxxxxxxxxxxx
# AGE-SECRET-KEY-xxxxxxxxxxxxxxxxxxxxx
1.2 Create .sops.yaml
SOPS requires a configuration file that specifies which KMS to use.
# Create .sops.yaml in your working directory
cat > .sops.yaml << 'EOF'
creation_rules:
- path_regex: .*
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxx # Replace with YOUR public key
EOF
# Extract your public key and replace it in .sops.yaml
PUBLIC_KEY=$(grep "^# public key:" ~/.age/sops-test-key.txt | sed 's/# public key: //')
cat > .sops.yaml << EOF
creation_rules:
- path_regex: .*
age: $PUBLIC_KEY
EOF
# Verify config is correct
cat .sops.yaml
1.3 Verify SOPS Configuration
# SOPS should be able to find the config
ls -la .sops.yaml
# Output: -rw-r--r-- 1 user staff 45 Dec 21 12:34 .sops.yaml
# View the config
cat .sops.yaml
Expected Output:
creation_rules:
- path_regex: .*
age: age1xxxxxxxxxxxxxxxxxxxxx
Test 2: Test SOPS Encryption Directly
2.1 Create a Test File
# Create plaintext YAML
echo 'secret: my-super-secret-password' > test-secret.yaml
# Verify content
cat test-secret.yaml
# Output: secret: my-super-secret-password
2.2 Encrypt with SOPS
# Tell SOPS where to find your Age key
export SOPS_AGE_KEY_FILE=~/.age/sops-test-key.txt
# Encrypt the file in-place
sops -e -i test-secret.yaml
# Verify it's encrypted (should be YAML with ENC[...])
cat test-secret.yaml
# Output should show: secret: ENC[AES256_GCM,data:xxxxx,iv:xxxxx,...]
2.3 Decrypt to Verify
# Decrypt and display (doesn't modify file)
sops -d test-secret.yaml
# Output:
# secret: my-super-secret-password
# sops:
# ...
# Extract just the secret value
sops -d test-secret.yaml | grep "^secret:" | sed 's/secret: //'
# Output: my-super-secret-password
2.4 Clean Up
# Remove test file
rm test-secret.yaml
Key Points:
- ✅ SOPS encrypts/decrypts correctly
- ✅ Plaintext is preserved during round-trip
- ✅
.sops.yamlcontrols which keys can decrypt
Test 3: Test typedialog Redaction (No Encryption Service)
Redaction works without any encryption - just replaces sensitive fields with [REDACTED].
# Test redaction (no encryption service needed)
# Provide input via stdin: username then password
echo -e "alice\nsecretpass123" | typedialog form examples/08-encryption/simple-login.toml \
--redact \
--format json
# You'll see prompts:
# Username *
# Password *
#
# Output:
# {
# "username": "alice",
# "password": "[REDACTED]"
# }
What's being tested:
- ✅ typedialog can detect sensitive fields
- ✅ Redaction replaces secrets with
[REDACTED] - ✅ Non-sensitive fields remain visible
Test 4: Test typedialog with Age Backend
Age backend encrypts locally without external service.
4.1 Ensure Age Key Exists
# Generate Age key if needed
if [ ! -f ~/.age/key.txt ]; then
age-keygen -o ~/.age/key.txt
fi
# Verify key exists
cat ~/.age/key.txt
4.2 Encrypt with Age Backend
# Provide input via stdin: username then password
echo -e "alice\nsecretpass123" | typedialog form examples/08-encryption/simple-login.toml \
--encrypt --backend age \
--key-file ~/.age/key.txt \
--format json
# Output:
# {
# "username": "alice",
# "password": "age1muz6ah54ew9am7mzmy0m4w5arcegt056l9448sqy5ju27q5qaf3qjv35tr"
# }
What's being tested:
- ✅ typedialog can encrypt with Age backend
- ✅ Ciphertext starts with
age1 - ✅ Non-sensitive fields remain plaintext
Test 5: Test typedialog with SOPS Backend ⭐
This is the main integration test.
5.1 Verify Setup
# Make sure .sops.yaml exists in current directory
cat .sops.yaml
# Should show your Age public key
# Set Age key for SOPS
export SOPS_AGE_KEY_FILE=~/.age/sops-test-key.txt
# Verify SOPS can find config
sops --version
5.2 Encrypt with SOPS Backend
# Provide input via stdin: username then password
echo -e "alice\nsecretpass123" | typedialog form examples/08-encryption/simple-login.toml \
--encrypt --backend sops \
--format json
# If .sops.yaml is not found, you'll see:
# SOPS encryption error: config file not found...
#
# If successful, output:
# {
# "username": "alice",
# "password": "sops:v1:4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a..."
# }
What's being tested:
- ✅ typedialog can use SOPS backend
- ✅ SOPS backend respects
.sops.yamlconfiguration - ✅ Ciphertext format is
sops:v1:<hex> - ✅ Sensitive fields are encrypted, plaintext fields remain visible
5.3 Verify SOPS Ciphertext
The encrypted password is hex-encoded encrypted YAML:
# Extract password from JSON
PASSWORD="sops:v1:4f5a6b7c8d9e0f1a2b3c..."
# Strip the version prefix
HEX_PART="4f5a6b7c8d9e0f1a2b3c..."
# Decode from hex to see raw encrypted content
echo "$HEX_PART" | xxd -r -p | head -c 100
# Shows SOPS-encrypted YAML structure
Test 6: Compare All Backends
Run the same form with different backends to see the difference.
6.1 Redaction
echo -e "alice\nsecretpass123" | typedialog form examples/08-encryption/simple-login.toml \
--redact --format json
# Output:
# {
# "username": "alice",
# "password": "[REDACTED]"
# }
6.2 Age
echo -e "alice\nsecretpass123" | typedialog form examples/08-encryption/simple-login.toml \
--encrypt --backend age --key-file ~/.age/key.txt --format json
# Output:
# {
# "username": "alice",
# "password": "age1xxxxxxxx..."
# }
6.3 SOPS
export SOPS_AGE_KEY_FILE=~/.age/sops-test-key.txt
echo -e "alice\nsecretpass123" | typedialog form examples/08-encryption/simple-login.toml \
--encrypt --backend sops --format json
# Output:
# {
# "username": "alice",
# "password": "sops:v1:4f5a6b..."
# }
Comparison:
| Backend | Format | Service Required | Use Case |
|---|---|---|---|
| Redaction | [REDACTED] |
No | Development, logging |
| Age | age1... |
No (local key) | Local development |
| SOPS | sops:v1:hex... |
No (Age) or Yes (AWS/GCP/Azure) | Team collaboration |
Test 7: Multi-Backend Form
Test a form with multiple encryption backends.
7.1 Create Test File
# Use the multi-backend example
cat examples/08-encryption/multi-backend-sops.toml
# This form demonstrates:
# - api_key: Age backend
# - db_password: SOPS backend
# - master_key: AWS KMS backend
7.2 Encrypt Different Fields with Different Backends
# Encrypt with SOPS (for db_password field)
echo -e "myapp\nproduction\nerror\ntestuser\ntestpass\napikey123" | \
typedialog form examples/08-encryption/multi-backend-sops.toml \
--encrypt --backend sops \
--format json
# Output shows:
# - api_key: field was encrypted (may show Error if backend not available)
# - db_password: encrypted with SOPS (sops:v1:...)
# - other fields: plain or encrypted based on field config
Test 8: Integration with encrypt Crate
Test that the Rust integration is working.
8.1 Run Cargo Tests
# Test SOPS backend in encrypt crate
cargo test -p encrypt --features sops --lib backend::sops
# Expected output:
# test backend::sops::tests::test_sops_backend_name ... ok
# test backend::sops::tests::test_sops_backend_info ... ok
# ... more tests ...
# test result: ok. 10 passed; 0 failed
8.2 Test Feature-Gating
# Test without SOPS feature
cargo test -p encrypt --features age test_is_available_sops
# Should show SOPS is not available when feature disabled
Troubleshooting
Problem: "config file not found"
# Error: config file not found, or has no creation rules
Cause: .sops.yaml not found
Solution:
# Check if .sops.yaml exists
ls -la .sops.yaml
# Create it if missing
cat > .sops.yaml << 'EOF'
creation_rules:
- path_regex: .*
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxx
EOF
# Or make sure you're in the correct directory
pwd
ls examples/08-encryption/
Problem: "no identity matched key"
# Error: no identity matched key
Cause: Age key not found or not accessible
Solution:
# Verify key file exists
cat ~/.age/sops-test-key.txt
# Set the key for SOPS
export SOPS_AGE_KEY_FILE=~/.age/sops-test-key.txt
# Test SOPS directly
sops -d test-secret.yaml
Problem: typedialog times out or hangs
# typedialog seems to be waiting for input
Cause: stdin not properly piped
Solution:
# Make sure to use echo -e with newlines
echo -e "username\npassword\nmore_input" | typedialog form ...
# Or use a here-document
typedialog form ... << EOF
alice
secretpass
EOF
Problem: "Backend 'sops' not available"
# Error: Backend 'sops' not available
Cause:
- sops binary not installed
- SOPS feature not compiled into typedialog
Solution:
# Install sops
brew install sops
# Rebuild typedialog with sops feature
cargo build --features encryption
# (should include sops by default)
# Or check if typedialog was built with SOPS
cargo build --features all
Summary: What Each Test Verifies
| Test | Verifies | Command |
|---|---|---|
| 1 | .sops.yaml configuration |
Manual file creation |
| 2 | SOPS encryption/decryption | sops -e -i / sops -d |
| 3 | typedialog redaction | --redact flag |
| 4 | typedialog + Age backend | --encrypt --backend age |
| 5 | typedialog + SOPS backend ⭐ | --encrypt --backend sops |
| 6 | Backend output format differences | Compare all three outputs |
| 7 | Multi-backend form support | Field-level backend config |
| 8 | Rust integration | cargo test --features sops |
Expected Timeline
- Setup: 5 minutes (create keys and .sops.yaml)
- Tests 1-4: 5 minutes each (quick manual tests)
- Test 5: 5 minutes (main SOPS integration test)
- Tests 6-8: 10 minutes (comparison and verification)
Total: ~45 minutes for complete integration testing
Next Steps
After verifying SOPS works:
-
Production Setup:
- Replace Age with AWS KMS in
.sops.yaml - Set up AWS credentials
- Deploy SOPS configuration
- Replace Age with AWS KMS in
-
Team Collaboration:
- Share
.sops.yamlin Git (does not contain secrets) - Each team member has their own KMS access
- SOPS handles key rotation automatically
- Share
-
CI/CD Integration:
- Store encrypted secrets in Git
- Decrypt during CI/CD pipeline
- Never expose plaintext secrets
-
Multi-Environment:
- Dev: Age backend (local)
- Staging: SOPS (shared team KMS)
- Prod: AWS KMS (automated)