fix: resolve critical security vulnerabilities in debug logging and command execution

- Remove sensitive data from debug logs (vault/secrets.go, secret/version.go)
- Add input validation for GPG key IDs and keychain item names
- Resolve GPG key IDs to full fingerprints before storing in metadata
- Add comprehensive test coverage for validation functions
- Add golangci-lint configuration with additional linters

Security improvements:
- Debug logs no longer expose decrypted secret values or private keys
- GPG and keychain commands now validate input to prevent injection attacks
- All validation uses precompiled regex patterns for performance
This commit is contained in:
2025-06-20 07:50:26 -07:00
parent 004dce5472
commit 985d79d3c0
8 changed files with 529 additions and 28 deletions

View File

@@ -189,21 +189,26 @@ Passphrase: ` + testPassphrase + `
}
t.Log("GPG key generated successfully")
// Get the key ID
// Get the key ID and fingerprint
output, err := runGPGWithPassphrase(gnupgHomeDir, testPassphrase,
[]string{"--list-secret-keys", "--with-colons"}, nil)
[]string{"--list-secret-keys", "--with-colons", "--fingerprint"}, nil)
if err != nil {
t.Fatalf("Failed to list GPG keys: %v", err)
}
// Parse output to get key ID
var keyID string
// Parse output to get key ID and fingerprint
var keyID, fingerprint string
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "sec:") {
fields := strings.Split(line, ":")
if len(fields) >= 5 {
keyID = fields[4]
}
} else if strings.HasPrefix(line, "fpr:") {
fields := strings.Split(line, ":")
if len(fields) >= 10 && fields[9] != "" {
fingerprint = fields[9]
break
}
}
@@ -212,7 +217,11 @@ Passphrase: ` + testPassphrase + `
if keyID == "" {
t.Fatalf("Failed to find GPG key ID in output: %s", output)
}
if fingerprint == "" {
t.Fatalf("Failed to find GPG fingerprint in output: %s", output)
}
t.Logf("Generated GPG key ID: %s", keyID)
t.Logf("Generated GPG fingerprint: %s", fingerprint)
// Set the GPG_AGENT_INFO to empty to ensure gpg-agent doesn't interfere
oldAgentInfo := os.Getenv("GPG_AGENT_INFO")
@@ -326,9 +335,9 @@ Passphrase: ` + testPassphrase + `
t.Errorf("Expected PGP unlock key type 'pgp', got '%s'", pgpUnlocker.GetType())
}
// Check if the key ID includes the GPG key ID
if !strings.Contains(pgpUnlocker.GetID(), keyID) {
t.Errorf("PGP unlock key ID '%s' does not contain GPG key ID '%s'", pgpUnlocker.GetID(), keyID)
// Check if the key ID includes the GPG fingerprint
if !strings.Contains(pgpUnlocker.GetID(), fingerprint) {
t.Errorf("PGP unlock key ID '%s' does not contain GPG fingerprint '%s'", pgpUnlocker.GetID(), fingerprint)
}
// Check if the key directory exists
@@ -400,8 +409,8 @@ Passphrase: ` + testPassphrase + `
t.Errorf("Expected metadata type 'pgp', got '%s'", metadata.Type)
}
if metadata.GPGKeyID != keyID {
t.Errorf("Expected GPG key ID '%s', got '%s'", keyID, metadata.GPGKeyID)
if metadata.GPGKeyID != fingerprint {
t.Errorf("Expected GPG fingerprint '%s', got '%s'", fingerprint, metadata.GPGKeyID)
}
})
@@ -431,7 +440,7 @@ Passphrase: ` + testPassphrase + `
pgpMetadata := PGPUnlockerMetadata{
UnlockerMetadata: metadata,
GPGKeyID: keyID,
GPGKeyID: fingerprint,
}
// Write metadata file
@@ -450,9 +459,9 @@ Passphrase: ` + testPassphrase + `
t.Fatalf("Failed to get GPG key ID: %v", err)
}
// Verify key ID
if retrievedKeyID != keyID {
t.Errorf("Expected GPG key ID '%s', got '%s'", keyID, retrievedKeyID)
// Verify key ID (should be the fingerprint)
if retrievedKeyID != fingerprint {
t.Errorf("Expected GPG fingerprint '%s', got '%s'", fingerprint, retrievedKeyID)
}
})