851 lines
31 KiB
Bash
Executable File
851 lines
31 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -e # Exit on any error
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Test configuration
|
|
TEST_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
|
TEST_PASSPHRASE="test-passphrase-123"
|
|
TEMP_DIR="$(mktemp -d)"
|
|
SECRET_BINARY="./secret"
|
|
|
|
# Enable debug output from the secret program
|
|
export GODEBUG="berlin.sneak.pkg.secret"
|
|
|
|
echo -e "${BLUE}=== Secret Manager Comprehensive Test Script ===${NC}"
|
|
echo -e "${YELLOW}Using temporary directory: $TEMP_DIR${NC}"
|
|
echo -e "${YELLOW}Debug output enabled: GODEBUG=$GODEBUG${NC}"
|
|
echo -e "${YELLOW}Note: All tests use environment variables (no manual input)${NC}"
|
|
|
|
# Function to print test steps
|
|
print_step() {
|
|
echo -e "\n${BLUE}Step $1: $2${NC}"
|
|
}
|
|
|
|
# Function to print success
|
|
print_success() {
|
|
echo -e "${GREEN}✓ $1${NC}"
|
|
}
|
|
|
|
# Function to print error and exit
|
|
print_error() {
|
|
echo -e "${RED}✗ $1${NC}"
|
|
exit 1
|
|
}
|
|
|
|
# Function to print warning (for expected failures)
|
|
print_warning() {
|
|
echo -e "${YELLOW}⚠ $1${NC}"
|
|
}
|
|
|
|
# Function to clear state directory and reset environment
|
|
reset_state() {
|
|
echo -e "${YELLOW}Resetting state directory...${NC}"
|
|
|
|
# Safety checks before removing anything
|
|
if [ -z "$TEMP_DIR" ]; then
|
|
print_error "TEMP_DIR is not set, cannot reset state safely"
|
|
fi
|
|
|
|
if [ ! -d "$TEMP_DIR" ]; then
|
|
print_error "TEMP_DIR ($TEMP_DIR) is not a directory, cannot reset state safely"
|
|
fi
|
|
|
|
# Additional safety: ensure TEMP_DIR looks like a temp directory
|
|
case "$TEMP_DIR" in
|
|
/tmp/* | /var/folders/* | */tmp/*)
|
|
# Looks like a reasonable temp directory path
|
|
;;
|
|
*)
|
|
print_error "TEMP_DIR ($TEMP_DIR) does not look like a safe temporary directory path"
|
|
;;
|
|
esac
|
|
|
|
# Now it's safe to remove contents - use find to avoid glob expansion issues
|
|
find "${TEMP_DIR:?}" -mindepth 1 -delete 2>/dev/null || true
|
|
unset SB_SECRET_MNEMONIC
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
export SB_SECRET_STATE_DIR="$TEMP_DIR"
|
|
}
|
|
|
|
# Cleanup function
|
|
cleanup() {
|
|
echo -e "\n${YELLOW}Cleaning up...${NC}"
|
|
rm -rf "$TEMP_DIR"
|
|
unset SB_SECRET_STATE_DIR
|
|
unset SB_SECRET_MNEMONIC
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
unset GODEBUG
|
|
echo -e "${GREEN}Cleanup complete${NC}"
|
|
}
|
|
|
|
# Set cleanup trap
|
|
trap cleanup EXIT
|
|
|
|
# Check that the secret binary exists
|
|
if [ ! -f "$SECRET_BINARY" ]; then
|
|
print_error "Secret binary not found at $SECRET_BINARY. Please run 'make build' first."
|
|
fi
|
|
|
|
# Test 1: Set up environment variables
|
|
print_step "1" "Setting up environment variables"
|
|
export SB_SECRET_STATE_DIR="$TEMP_DIR"
|
|
export SB_SECRET_MNEMONIC="$TEST_MNEMONIC"
|
|
print_success "Environment variables set"
|
|
echo " SB_SECRET_STATE_DIR=$SB_SECRET_STATE_DIR"
|
|
echo " SB_SECRET_MNEMONIC=$TEST_MNEMONIC"
|
|
|
|
# Test 2: Initialize the secret manager (should create default vault)
|
|
print_step "2" "Initializing secret manager (creates default vault)"
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
echo " SB_UNLOCK_PASSPHRASE=$SB_UNLOCK_PASSPHRASE"
|
|
|
|
# Verify environment variables are exported and visible to subprocesses
|
|
echo "Verifying environment variables are exported:"
|
|
env | grep -E "^SB_" || true
|
|
|
|
echo "Running: $SECRET_BINARY init"
|
|
# Run with explicit environment to ensure variables are passed
|
|
if SB_SECRET_STATE_DIR="$SB_SECRET_STATE_DIR" \
|
|
SB_SECRET_MNEMONIC="$SB_SECRET_MNEMONIC" \
|
|
SB_UNLOCK_PASSPHRASE="$SB_UNLOCK_PASSPHRASE" \
|
|
GODEBUG="$GODEBUG" \
|
|
$SECRET_BINARY init </dev/null; then
|
|
print_success "Secret manager initialized with default vault"
|
|
else
|
|
print_error "Failed to initialize secret manager"
|
|
fi
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
|
|
# Verify directory structure was created
|
|
if [ -d "$TEMP_DIR" ]; then
|
|
print_success "State directory created: $TEMP_DIR"
|
|
else
|
|
print_error "State directory was not created"
|
|
fi
|
|
|
|
# Test 3: Vault management
|
|
print_step "3" "Testing vault management"
|
|
|
|
# List vaults (should show default)
|
|
echo "Listing vaults..."
|
|
echo "Running: $SECRET_BINARY vault list"
|
|
if $SECRET_BINARY vault list; then
|
|
VAULTS=$($SECRET_BINARY vault list)
|
|
echo "Available vaults: $VAULTS"
|
|
print_success "Listed vaults successfully"
|
|
else
|
|
print_error "Failed to list vaults"
|
|
fi
|
|
|
|
# Create a new vault
|
|
echo "Creating new vault 'work'..."
|
|
echo "Running: $SECRET_BINARY vault create work"
|
|
if $SECRET_BINARY vault create work; then
|
|
print_success "Created vault 'work'"
|
|
else
|
|
print_error "Failed to create vault 'work'"
|
|
fi
|
|
|
|
# Create another vault
|
|
echo "Creating new vault 'personal'..."
|
|
echo "Running: $SECRET_BINARY vault create personal"
|
|
if $SECRET_BINARY vault create personal; then
|
|
print_success "Created vault 'personal'"
|
|
else
|
|
print_error "Failed to create vault 'personal'"
|
|
fi
|
|
|
|
# List vaults again (should show default, work, personal)
|
|
echo "Listing vaults after creation..."
|
|
echo "Running: $SECRET_BINARY vault list"
|
|
if $SECRET_BINARY vault list; then
|
|
VAULTS=$($SECRET_BINARY vault list)
|
|
echo "Available vaults: $VAULTS"
|
|
print_success "Listed vaults after creation"
|
|
else
|
|
print_error "Failed to list vaults after creation"
|
|
fi
|
|
|
|
# Switch to work vault
|
|
echo "Switching to 'work' vault..."
|
|
echo "Running: $SECRET_BINARY vault select work"
|
|
if $SECRET_BINARY vault select work; then
|
|
print_success "Switched to 'work' vault"
|
|
else
|
|
print_error "Failed to switch to 'work' vault"
|
|
fi
|
|
|
|
# Test 4: Import functionality with environment variable combinations
|
|
print_step "4" "Testing import functionality with environment variable combinations"
|
|
|
|
# Test 4a: Import with both env vars set (typical usage)
|
|
echo -e "\n${YELLOW}Test 4a: Import with both SB_SECRET_MNEMONIC and SB_UNLOCK_PASSPHRASE set${NC}"
|
|
reset_state
|
|
export SB_SECRET_MNEMONIC="$TEST_MNEMONIC"
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
|
|
# Create a vault first
|
|
echo "Running: $SECRET_BINARY vault create test-vault"
|
|
if $SECRET_BINARY vault create test-vault; then
|
|
print_success "Created test-vault for import testing"
|
|
else
|
|
print_error "Failed to create test-vault"
|
|
fi
|
|
|
|
# Import should work without prompts
|
|
echo "Importing with both env vars set (automated)..."
|
|
echo "Running: $SECRET_BINARY vault import test-vault"
|
|
if $SECRET_BINARY vault import test-vault; then
|
|
print_success "Import succeeded with both env vars (automated)"
|
|
else
|
|
print_error "Import failed with both env vars"
|
|
fi
|
|
|
|
# Test 4b: Import into non-existent vault (should fail)
|
|
echo -e "\n${YELLOW}Test 4b: Import into non-existent vault (should fail)${NC}"
|
|
echo "Importing into non-existent vault (should fail)..."
|
|
if $SECRET_BINARY vault import nonexistent-vault; then
|
|
print_error "Import should have failed for non-existent vault"
|
|
else
|
|
print_success "Import correctly failed for non-existent vault"
|
|
fi
|
|
|
|
# Test 4c: Import with invalid mnemonic (should fail)
|
|
echo -e "\n${YELLOW}Test 4c: Import with invalid mnemonic (should fail)${NC}"
|
|
export SB_SECRET_MNEMONIC="invalid mnemonic phrase that should not work"
|
|
|
|
# Create a vault first
|
|
echo "Running: $SECRET_BINARY vault create test-vault2"
|
|
if $SECRET_BINARY vault create test-vault2; then
|
|
print_success "Created test-vault2 for invalid mnemonic testing"
|
|
else
|
|
print_error "Failed to create test-vault2"
|
|
fi
|
|
|
|
echo "Importing with invalid mnemonic (should fail)..."
|
|
if $SECRET_BINARY vault import test-vault2; then
|
|
print_error "Import should have failed with invalid mnemonic"
|
|
else
|
|
print_success "Import correctly failed with invalid mnemonic"
|
|
fi
|
|
|
|
# Reset state for remaining tests
|
|
reset_state
|
|
export SB_SECRET_MNEMONIC="$TEST_MNEMONIC"
|
|
|
|
# Test 5: Unlocker management
|
|
print_step "5" "Testing unlocker management"
|
|
|
|
# Initialize with mnemonic and passphrase
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
echo "Running: $SECRET_BINARY init (with SB_SECRET_MNEMONIC and SB_UNLOCK_PASSPHRASE set)"
|
|
if $SECRET_BINARY init; then
|
|
print_success "Initialized for unlocker testing"
|
|
else
|
|
print_error "Failed to initialize for unlocker testing"
|
|
fi
|
|
|
|
# Create passphrase-protected unlocker
|
|
echo "Creating passphrase-protected unlocker..."
|
|
echo "Running: $SECRET_BINARY unlockers add passphrase (with SB_UNLOCK_PASSPHRASE set)"
|
|
if $SECRET_BINARY unlockers add passphrase; then
|
|
print_success "Created passphrase-protected unlocker"
|
|
else
|
|
print_error "Failed to create passphrase-protected unlocker"
|
|
exit 1
|
|
fi
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
|
|
# List unlockers
|
|
echo "Listing unlockers..."
|
|
echo "Running: $SECRET_BINARY unlockers list"
|
|
if $SECRET_BINARY unlockers list; then
|
|
UNLOCKERS=$($SECRET_BINARY unlockers list)
|
|
echo "Available unlockers: $UNLOCKERS"
|
|
print_success "Listed unlockers"
|
|
else
|
|
print_error "Failed to list unlockers"
|
|
exit 1
|
|
fi
|
|
|
|
# Test 6: Secret management with mnemonic (keyless operation)
|
|
print_step "6" "Testing mnemonic-based secret operations (keyless)"
|
|
|
|
# Add secrets using mnemonic (no unlocker required)
|
|
echo "Adding secrets using mnemonic-based long-term key..."
|
|
|
|
# Test secret 1
|
|
echo "Running: echo \"my-super-secret-password\" | $SECRET_BINARY add \"database/password\""
|
|
if echo "my-super-secret-password" | $SECRET_BINARY add "database/password"; then
|
|
print_success "Added secret: database/password"
|
|
else
|
|
print_error "Failed to add secret: database/password"
|
|
fi
|
|
|
|
# Test secret 2
|
|
echo "Running: echo \"api-key-12345\" | $SECRET_BINARY add \"api/key\""
|
|
if echo "api-key-12345" | $SECRET_BINARY add "api/key"; then
|
|
print_success "Added secret: api/key"
|
|
else
|
|
print_error "Failed to add secret: api/key"
|
|
fi
|
|
|
|
# Test secret 3 (with path)
|
|
echo "Running: echo \"ssh-private-key-content\" | $SECRET_BINARY add \"ssh/private-key\""
|
|
if echo "ssh-private-key-content" | $SECRET_BINARY add "ssh/private-key"; then
|
|
print_success "Added secret: ssh/private-key"
|
|
else
|
|
print_error "Failed to add secret: ssh/private-key"
|
|
fi
|
|
|
|
# Test secret 4 (with dots and underscores)
|
|
echo "Running: echo \"jwt-secret-token\" | $SECRET_BINARY add \"app.config_jwt_secret\""
|
|
if echo "jwt-secret-token" | $SECRET_BINARY add "app.config_jwt_secret"; then
|
|
print_success "Added secret: app.config_jwt_secret"
|
|
else
|
|
print_error "Failed to add secret: app.config_jwt_secret"
|
|
fi
|
|
|
|
# Retrieve secrets using mnemonic
|
|
echo "Retrieving secrets using mnemonic-based long-term key..."
|
|
|
|
# Retrieve and verify secret 1
|
|
RETRIEVED_SECRET1=$($SECRET_BINARY get "database/password" 2>/dev/null)
|
|
if [ "$RETRIEVED_SECRET1" = "my-super-secret-password" ]; then
|
|
print_success "Retrieved and verified secret: database/password"
|
|
else
|
|
print_error "Failed to retrieve or verify secret: database/password"
|
|
fi
|
|
|
|
# Retrieve and verify secret 2
|
|
RETRIEVED_SECRET2=$($SECRET_BINARY get "api/key" 2>/dev/null)
|
|
if [ "$RETRIEVED_SECRET2" = "api-key-12345" ]; then
|
|
print_success "Retrieved and verified secret: api/key"
|
|
else
|
|
print_error "Failed to retrieve or verify secret: api/key"
|
|
fi
|
|
|
|
# Retrieve and verify secret 3
|
|
RETRIEVED_SECRET3=$($SECRET_BINARY get "ssh/private-key" 2>/dev/null)
|
|
if [ "$RETRIEVED_SECRET3" = "ssh-private-key-content" ]; then
|
|
print_success "Retrieved and verified secret: ssh/private-key"
|
|
else
|
|
print_error "Failed to retrieve or verify secret: ssh/private-key"
|
|
fi
|
|
|
|
# List all secrets
|
|
echo "Listing all secrets..."
|
|
echo "Running: $SECRET_BINARY list"
|
|
if $SECRET_BINARY list; then
|
|
SECRETS=$($SECRET_BINARY list)
|
|
echo "Secrets in current vault:"
|
|
echo "$SECRETS" | while read -r secret; do
|
|
echo " - $secret"
|
|
done
|
|
print_success "Listed all secrets"
|
|
else
|
|
print_error "Failed to list secrets"
|
|
fi
|
|
|
|
# Test 7: Testing vault operations with different unlockers
|
|
print_step "7" "Testing vault operations with passphrase unlocker"
|
|
|
|
# Create a new vault for unlocker testing
|
|
echo "Running: $SECRET_BINARY vault create traditional"
|
|
$SECRET_BINARY vault create traditional
|
|
|
|
# Import mnemonic into the traditional vault (required for versioned secrets)
|
|
echo "Importing mnemonic into traditional vault..."
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
echo "Running: $SECRET_BINARY vault import traditional"
|
|
if $SECRET_BINARY vault import traditional; then
|
|
print_success "Imported mnemonic into traditional vault"
|
|
else
|
|
print_error "Failed to import mnemonic into traditional vault"
|
|
fi
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
|
|
# Now add a secret using the vault with unlocker
|
|
echo "Adding secret to vault with unlocker..."
|
|
echo "Running: echo 'traditional-secret' | $SECRET_BINARY add traditional/secret"
|
|
if echo "traditional-secret" | $SECRET_BINARY add traditional/secret; then
|
|
print_success "Added secret to vault with unlocker"
|
|
else
|
|
print_error "Failed to add secret to vault with unlocker"
|
|
fi
|
|
|
|
# Retrieve secret using passphrase (temporarily unset mnemonic to test unlocker)
|
|
echo "Retrieving secret from vault with unlocker..."
|
|
TEMP_MNEMONIC="$SB_SECRET_MNEMONIC"
|
|
unset SB_SECRET_MNEMONIC
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
echo "Running: $SECRET_BINARY get traditional/secret (using passphrase unlocker)"
|
|
if RETRIEVED=$($SECRET_BINARY get traditional/secret 2>&1); then
|
|
print_success "Retrieved: $RETRIEVED"
|
|
else
|
|
print_error "Failed to retrieve secret from vault with unlocker"
|
|
fi
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
export SB_SECRET_MNEMONIC="$TEMP_MNEMONIC"
|
|
|
|
# Test 8: Advanced unlocker management
|
|
print_step "8" "Testing advanced unlocker management"
|
|
|
|
if [ "$PLATFORM" = "darwin" ]; then
|
|
# macOS only: Test Secure Enclave
|
|
echo "Testing Secure Enclave unlocker creation..."
|
|
if $SECRET_BINARY unlockers add sep; then
|
|
print_success "Created Secure Enclave unlocker"
|
|
else
|
|
print_warning "Secure Enclave unlocker creation not yet implemented"
|
|
fi
|
|
fi
|
|
|
|
# Get current unlocker ID for testing
|
|
echo "Getting current unlocker for testing..."
|
|
echo "Running: $SECRET_BINARY unlockers list"
|
|
if $SECRET_BINARY unlockers list; then
|
|
CURRENT_UNLOCKER_ID=$($SECRET_BINARY unlockers list | head -n1 | awk '{print $1}')
|
|
if [ -n "$CURRENT_UNLOCKER_ID" ]; then
|
|
print_success "Found unlocker ID: $CURRENT_UNLOCKER_ID"
|
|
|
|
# Test unlocker selection
|
|
echo "Testing unlocker selection..."
|
|
echo "Running: $SECRET_BINARY unlocker select $CURRENT_UNLOCKER_ID"
|
|
if $SECRET_BINARY unlocker select "$CURRENT_UNLOCKER_ID"; then
|
|
print_success "Selected unlocker: $CURRENT_UNLOCKER_ID"
|
|
else
|
|
print_warning "Unlocker selection not yet implemented"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Test 9: Secret name validation and edge cases
|
|
print_step "9" "Testing secret name validation and edge cases"
|
|
|
|
# Switch back to default vault for name validation tests
|
|
echo "Switching back to default vault..."
|
|
$SECRET_BINARY vault select default
|
|
|
|
# Test valid names
|
|
VALID_NAMES=("valid-name" "valid.name" "valid_name" "valid/path/name" "123valid" "a" "very-long-name-with-many-parts/and/paths")
|
|
for name in "${VALID_NAMES[@]}"; do
|
|
echo "Running: echo \"test-value\" | $SECRET_BINARY add $name --force"
|
|
if echo "test-value" | $SECRET_BINARY add "$name" --force; then
|
|
print_success "Valid name accepted: $name"
|
|
else
|
|
print_error "Valid name rejected: $name"
|
|
fi
|
|
done
|
|
|
|
# Test invalid names (these should fail)
|
|
echo "Testing invalid names (should fail)..."
|
|
INVALID_NAMES=("Invalid-Name" "invalid name" "invalid@name" "invalid#name" "invalid%name" "")
|
|
for name in "${INVALID_NAMES[@]}"; do
|
|
echo "Running: echo \"test-value\" | $SECRET_BINARY add $name"
|
|
if echo "test-value" | $SECRET_BINARY add "$name"; then
|
|
print_error "Invalid name accepted (should have been rejected): '$name'"
|
|
else
|
|
print_success "Invalid name correctly rejected: '$name'"
|
|
fi
|
|
done
|
|
|
|
# Test 10: Overwrite protection and force flag
|
|
print_step "10" "Testing overwrite protection and force flag"
|
|
|
|
# Try to add existing secret without --force (should fail)
|
|
echo "Running: echo \"new-value\" | $SECRET_BINARY add \"database/password\""
|
|
if echo "new-value" | $SECRET_BINARY add "database/password"; then
|
|
print_error "Overwrite protection failed - secret was overwritten without --force"
|
|
else
|
|
print_success "Overwrite protection working - secret not overwritten without --force"
|
|
fi
|
|
|
|
# Try to add existing secret with --force (should succeed)
|
|
echo "Running: echo \"new-password-value\" | $SECRET_BINARY add \"database/password\" --force"
|
|
if echo "new-password-value" | $SECRET_BINARY add "database/password" --force; then
|
|
print_success "Force overwrite working - secret overwritten with --force"
|
|
|
|
# Verify the new value
|
|
RETRIEVED_NEW=$($SECRET_BINARY get "database/password" 2>/dev/null)
|
|
if [ "$RETRIEVED_NEW" = "new-password-value" ]; then
|
|
print_success "Overwritten secret has correct new value"
|
|
else
|
|
print_error "Overwritten secret has incorrect value"
|
|
fi
|
|
else
|
|
print_error "Force overwrite failed - secret not overwritten with --force"
|
|
fi
|
|
|
|
# Test 11: Cross-vault operations
|
|
print_step "11" "Testing cross-vault operations"
|
|
|
|
# First create and import mnemonic into work vault since it was destroyed by reset_state
|
|
echo "Creating work vault for cross-vault testing..."
|
|
echo "Running: $SECRET_BINARY vault create work"
|
|
if $SECRET_BINARY vault create work; then
|
|
print_success "Created work vault for cross-vault testing"
|
|
else
|
|
print_error "Failed to create work vault for cross-vault testing"
|
|
fi
|
|
|
|
# Import mnemonic into work vault so it can store secrets
|
|
echo "Importing mnemonic into work vault..."
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
echo "Running: $SECRET_BINARY vault import work"
|
|
if $SECRET_BINARY vault import work; then
|
|
print_success "Imported mnemonic into work vault"
|
|
else
|
|
print_error "Failed to import mnemonic into work vault"
|
|
fi
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
|
|
# Switch to work vault and add secrets there
|
|
echo "Switching to 'work' vault for cross-vault testing..."
|
|
echo "Running: $SECRET_BINARY vault select work"
|
|
if $SECRET_BINARY vault select work; then
|
|
print_success "Switched to 'work' vault"
|
|
|
|
# Add work-specific secrets
|
|
echo "Running: echo \"work-database-password\" | $SECRET_BINARY add \"work/database\""
|
|
if echo "work-database-password" | $SECRET_BINARY add "work/database"; then
|
|
print_success "Added work-specific secret"
|
|
else
|
|
print_error "Failed to add work-specific secret"
|
|
fi
|
|
|
|
# List secrets in work vault
|
|
echo "Running: $SECRET_BINARY list"
|
|
if $SECRET_BINARY list; then
|
|
WORK_SECRETS=$($SECRET_BINARY list)
|
|
echo "Secrets in work vault: $WORK_SECRETS"
|
|
print_success "Listed work vault secrets"
|
|
else
|
|
print_error "Failed to list work vault secrets"
|
|
fi
|
|
else
|
|
print_error "Failed to switch to 'work' vault"
|
|
fi
|
|
|
|
# Switch back to default vault
|
|
echo "Switching back to 'default' vault..."
|
|
echo "Running: $SECRET_BINARY vault select default"
|
|
if $SECRET_BINARY vault select default; then
|
|
print_success "Switched back to 'default' vault"
|
|
|
|
# Verify default vault secrets are still there
|
|
echo "Running: $SECRET_BINARY get \"database/password\""
|
|
if $SECRET_BINARY get "database/password"; then
|
|
print_success "Default vault secrets still accessible"
|
|
else
|
|
print_error "Default vault secrets not accessible"
|
|
fi
|
|
else
|
|
print_error "Failed to switch back to 'default' vault"
|
|
fi
|
|
|
|
# Test 12: File structure verification
|
|
print_step "12" "Verifying file structure"
|
|
|
|
echo "Checking file structure in $TEMP_DIR..."
|
|
if [ -d "$TEMP_DIR/vaults.d/default/secrets.d" ]; then
|
|
print_success "Default vault structure exists"
|
|
|
|
# Check a specific secret's file structure
|
|
SECRET_DIR="$TEMP_DIR/vaults.d/default/secrets.d/database%password"
|
|
if [ -d "$SECRET_DIR" ]; then
|
|
print_success "Secret directory exists: database%password"
|
|
|
|
# Check for versions directory and current symlink
|
|
if [ -d "$SECRET_DIR/versions" ]; then
|
|
print_success "Versions directory exists"
|
|
else
|
|
print_error "Versions directory missing"
|
|
fi
|
|
|
|
if [ -L "$SECRET_DIR/current" ] || [ -f "$SECRET_DIR/current" ]; then
|
|
print_success "Current version symlink exists"
|
|
else
|
|
print_error "Current version symlink missing"
|
|
fi
|
|
|
|
# Check version directory structure
|
|
LATEST_VERSION=$(ls -1 "$SECRET_DIR/versions" 2>/dev/null | sort -r | head -n1)
|
|
if [ -n "$LATEST_VERSION" ]; then
|
|
VERSION_DIR="$SECRET_DIR/versions/$LATEST_VERSION"
|
|
print_success "Found version directory: $LATEST_VERSION"
|
|
|
|
# Check required files in version directory
|
|
VERSION_FILES=("value.age" "pub.age" "priv.age" "metadata.age")
|
|
for file in "${VERSION_FILES[@]}"; do
|
|
if [ -f "$VERSION_DIR/$file" ]; then
|
|
print_success "Version file exists: $file"
|
|
else
|
|
print_error "Version file missing: $file"
|
|
fi
|
|
done
|
|
else
|
|
print_error "No version directories found"
|
|
fi
|
|
else
|
|
print_error "Secret directory not found"
|
|
fi
|
|
else
|
|
print_error "Default vault structure not found"
|
|
fi
|
|
|
|
# Check work vault structure
|
|
if [ -d "$TEMP_DIR/vaults.d/work" ]; then
|
|
print_success "Work vault structure exists"
|
|
else
|
|
print_error "Work vault structure not found"
|
|
fi
|
|
|
|
# Check configuration files
|
|
if [ -f "$TEMP_DIR/configuration.json" ]; then
|
|
print_success "Global configuration file exists"
|
|
else
|
|
print_warning "Global configuration file not found (may not be implemented yet)"
|
|
fi
|
|
|
|
# Check current vault symlink
|
|
if [ -L "$TEMP_DIR/currentvault" ] || [ -f "$TEMP_DIR/currentvault" ]; then
|
|
print_success "Current vault link exists"
|
|
else
|
|
print_error "Current vault link not found"
|
|
fi
|
|
|
|
# Test 13: Environment variable error handling
|
|
print_step "13" "Testing environment variable error handling"
|
|
|
|
# Test with non-existent state directory
|
|
export SB_SECRET_STATE_DIR="$TEMP_DIR/nonexistent/directory"
|
|
echo "Running: $SECRET_BINARY get \"database/password\""
|
|
if $SECRET_BINARY get "database/password"; then
|
|
print_error "Should have failed with non-existent state directory"
|
|
else
|
|
print_success "Correctly failed with non-existent state directory"
|
|
fi
|
|
|
|
# Test init with non-existent directory (should work)
|
|
echo "Running: $SECRET_BINARY init (with SB_UNLOCK_PASSPHRASE set)"
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
if $SECRET_BINARY init; then
|
|
print_success "Init works with non-existent state directory"
|
|
else
|
|
print_error "Init should work with non-existent state directory"
|
|
fi
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
|
|
# Reset to working directory
|
|
export SB_SECRET_STATE_DIR="$TEMP_DIR"
|
|
|
|
# Test 14: Mixed approach compatibility
|
|
print_step "14" "Testing mixed approach compatibility"
|
|
|
|
# Switch to traditional vault and test access with passphrase
|
|
echo "Switching to traditional vault..."
|
|
$SECRET_BINARY vault select traditional
|
|
|
|
# Verify passphrase can access traditional vault secrets
|
|
unset SB_SECRET_MNEMONIC
|
|
export SB_UNLOCK_PASSPHRASE="$TEST_PASSPHRASE"
|
|
RETRIEVED_MIXED=$($SECRET_BINARY get "traditional/secret" 2>/dev/null)
|
|
unset SB_UNLOCK_PASSPHRASE
|
|
export SB_SECRET_MNEMONIC="$TEST_MNEMONIC"
|
|
|
|
if [ "$RETRIEVED_MIXED" = "traditional-secret" ]; then
|
|
print_success "Passphrase unlocker can access vault secrets"
|
|
else
|
|
print_error "Failed to access secret from traditional vault (expected: traditional-secret, got: $RETRIEVED_MIXED)"
|
|
fi
|
|
|
|
# Switch back to default vault
|
|
$SECRET_BINARY vault select default
|
|
|
|
# Test without mnemonic but with unlocker
|
|
echo "Testing mnemonic-created vault access..."
|
|
echo "Testing traditional unlocker access to mnemonic-created secrets..."
|
|
echo "Running: $SECRET_BINARY get test/seed (with mnemonic set)"
|
|
if RETRIEVED=$($SECRET_BINARY get test/seed 2>&1); then
|
|
print_success "Traditional unlocker can access mnemonic-created secrets"
|
|
else
|
|
print_warning "Traditional unlocker cannot access mnemonic-created secrets (may need implementation)"
|
|
fi
|
|
|
|
# Re-enable mnemonic for final tests
|
|
export SB_SECRET_MNEMONIC="$TEST_MNEMONIC"
|
|
|
|
# Test 15: Version management
|
|
print_step "15" "Testing version management"
|
|
|
|
# Switch back to default vault for version testing
|
|
echo "Switching to default vault for version testing..."
|
|
echo "Running: $SECRET_BINARY vault select default"
|
|
$SECRET_BINARY vault select default
|
|
|
|
# Test listing versions of a secret
|
|
echo "Listing versions of database/password..."
|
|
echo "Running: $SECRET_BINARY version list \"database/password\""
|
|
if $SECRET_BINARY version list "database/password"; then
|
|
print_success "Listed versions of database/password"
|
|
else
|
|
print_error "Failed to list versions of database/password"
|
|
fi
|
|
|
|
# Add a new version of an existing secret
|
|
echo "Adding new version of database/password..."
|
|
echo "Running: echo \"version-2-password\" | $SECRET_BINARY add \"database/password\" --force"
|
|
if echo "version-2-password" | $SECRET_BINARY add "database/password" --force; then
|
|
print_success "Added new version of database/password"
|
|
|
|
# List versions again to see both
|
|
echo "Running: $SECRET_BINARY version list \"database/password\""
|
|
if $SECRET_BINARY version list "database/password"; then
|
|
print_success "Listed versions after adding new version"
|
|
else
|
|
print_error "Failed to list versions after adding new version"
|
|
fi
|
|
else
|
|
print_error "Failed to add new version of database/password"
|
|
fi
|
|
|
|
# Get current version (should be the latest)
|
|
echo "Getting current version of database/password..."
|
|
CURRENT_VALUE=$($SECRET_BINARY get "database/password" 2>/dev/null)
|
|
if [ "$CURRENT_VALUE" = "version-2-password" ]; then
|
|
print_success "Current version has correct value"
|
|
else
|
|
print_error "Current version has incorrect value"
|
|
fi
|
|
|
|
# Get specific version by capturing version from list output
|
|
echo "Getting specific version of database/password..."
|
|
VERSIONS=$($SECRET_BINARY version list "database/password" | grep -E '^[0-9]{8}\.[0-9]{3}' | awk '{print $1}')
|
|
FIRST_VERSION=$(echo "$VERSIONS" | tail -n1)
|
|
if [ -n "$FIRST_VERSION" ]; then
|
|
echo "Running: $SECRET_BINARY get --version $FIRST_VERSION \"database/password\""
|
|
VERSIONED_VALUE=$($SECRET_BINARY get --version "$FIRST_VERSION" "database/password" 2>/dev/null)
|
|
if [ "$VERSIONED_VALUE" = "my-super-secret-password" ]; then
|
|
print_success "Retrieved correct value from specific version"
|
|
else
|
|
print_error "Retrieved incorrect value from specific version (expected: my-super-secret-password, got: $VERSIONED_VALUE)"
|
|
fi
|
|
else
|
|
print_error "Could not determine version to test"
|
|
fi
|
|
|
|
# Test version promotion
|
|
echo "Testing version promotion..."
|
|
if [ -n "$FIRST_VERSION" ]; then
|
|
echo "Running: $SECRET_BINARY version promote \"database/password\" $FIRST_VERSION"
|
|
if $SECRET_BINARY version promote "database/password" "$FIRST_VERSION"; then
|
|
print_success "Promoted older version to current"
|
|
|
|
# Verify the promoted version is now current
|
|
PROMOTED_VALUE=$($SECRET_BINARY get "database/password" 2>/dev/null)
|
|
if [ "$PROMOTED_VALUE" = "my-super-secret-password" ]; then
|
|
print_success "Promoted version is now current"
|
|
else
|
|
print_error "Promoted version value is incorrect (expected: my-super-secret-password, got: $PROMOTED_VALUE)"
|
|
fi
|
|
else
|
|
print_error "Failed to promote version"
|
|
fi
|
|
fi
|
|
|
|
# Check version directory structure
|
|
echo "Checking version directory structure..."
|
|
VERSION_DIR="$TEMP_DIR/vaults.d/default/secrets.d/database%password/versions"
|
|
if [ -d "$VERSION_DIR" ]; then
|
|
print_success "Versions directory exists"
|
|
|
|
# Count version directories
|
|
VERSION_COUNT=$(find "$VERSION_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
|
|
if [ "$VERSION_COUNT" -ge 2 ]; then
|
|
print_success "Multiple version directories found: $VERSION_COUNT"
|
|
else
|
|
print_error "Expected multiple version directories, found: $VERSION_COUNT"
|
|
fi
|
|
|
|
# Check for current symlink
|
|
CURRENT_LINK="$TEMP_DIR/vaults.d/default/secrets.d/database%password/current"
|
|
if [ -L "$CURRENT_LINK" ] || [ -f "$CURRENT_LINK" ]; then
|
|
print_success "Current version symlink exists"
|
|
else
|
|
print_error "Current version symlink not found"
|
|
fi
|
|
else
|
|
print_error "Versions directory not found"
|
|
fi
|
|
|
|
# Final summary
|
|
echo -e "\n${GREEN}=== Test Summary ===${NC}"
|
|
echo -e "${GREEN}✓ Environment variable support (SB_SECRET_STATE_DIR, SB_SECRET_MNEMONIC)${NC}"
|
|
echo -e "${GREEN}✓ Secret manager initialization${NC}"
|
|
echo -e "${GREEN}✓ Vault management (create, list, select)${NC}"
|
|
echo -e "${GREEN}✓ Import functionality with environment variable combinations${NC}"
|
|
echo -e "${GREEN}✓ Import error handling (non-existent vault, invalid mnemonic)${NC}"
|
|
echo -e "${GREEN}✓ Unlocker management (passphrase, PGP, SEP)${NC}"
|
|
echo -e "${GREEN}✓ Secret generation and storage${NC}"
|
|
echo -e "${GREEN}✓ Vault operations with passphrase unlocker${NC}"
|
|
echo -e "${GREEN}✓ Secret name validation${NC}"
|
|
echo -e "${GREEN}✓ Overwrite protection and force flag${NC}"
|
|
echo -e "${GREEN}✓ Cross-vault operations${NC}"
|
|
echo -e "${GREEN}✓ Per-secret key file structure${NC}"
|
|
echo -e "${GREEN}✓ Mixed approach compatibility${NC}"
|
|
echo -e "${GREEN}✓ Error handling${NC}"
|
|
echo -e "${GREEN}✓ Version management (list, get, promote)${NC}"
|
|
|
|
echo -e "\n${GREEN}🎉 Comprehensive test completed with environment variable automation!${NC}"
|
|
|
|
# Show usage examples for all implemented functionality
|
|
echo -e "\n${BLUE}=== Complete Usage Examples ===${NC}"
|
|
echo -e "${YELLOW}# Environment setup:${NC}"
|
|
echo "export SB_SECRET_STATE_DIR=\"/path/to/your/secrets\""
|
|
echo "export SB_SECRET_MNEMONIC=\"your twelve word mnemonic phrase here\""
|
|
echo ""
|
|
echo -e "${YELLOW}# Initialization:${NC}"
|
|
echo "secret init"
|
|
echo ""
|
|
echo -e "${YELLOW}# Vault management:${NC}"
|
|
echo "secret vault list"
|
|
echo "secret vault create work"
|
|
echo "secret vault select work"
|
|
echo ""
|
|
echo -e "${YELLOW}# Import mnemonic (automated with environment variables):${NC}"
|
|
echo "export SB_SECRET_MNEMONIC=\"abandon abandon...\""
|
|
echo "export SB_UNLOCK_PASSPHRASE=\"passphrase\""
|
|
echo "secret vault import work"
|
|
echo ""
|
|
echo -e "${YELLOW}# Unlocker management:${NC}"
|
|
echo "$SECRET_BINARY unlockers add <type> # Add unlocker (passphrase, pgp, keychain)"
|
|
echo "$SECRET_BINARY unlockers add passphrase"
|
|
echo "$SECRET_BINARY unlockers add pgp <gpg-key-id>"
|
|
echo "$SECRET_BINARY unlockers add keychain # macOS only"
|
|
echo "$SECRET_BINARY unlockers list # List all unlockers"
|
|
echo "$SECRET_BINARY unlocker select <unlocker-id> # Select current unlocker"
|
|
echo "$SECRET_BINARY unlockers rm <unlocker-id> # Remove unlocker"
|
|
echo ""
|
|
echo -e "${YELLOW}# Secret management:${NC}"
|
|
echo "echo \"my-secret\" | secret add \"app/password\""
|
|
echo "echo \"my-secret\" | secret add \"app/password\" --force"
|
|
echo "secret get \"app/password\""
|
|
echo "secret get --version 20231215.001 \"app/password\""
|
|
echo "secret list"
|
|
echo ""
|
|
echo -e "${YELLOW}# Version management:${NC}"
|
|
echo "secret version list \"app/password\""
|
|
echo "secret version promote \"app/password\" 20231215.001"
|
|
echo ""
|
|
echo -e "${YELLOW}# Cross-vault operations:${NC}"
|
|
echo "secret vault select work"
|
|
echo "echo \"work-secret\" | secret add \"work/database\""
|
|
echo "secret vault select default" |