Fix for double encryption of secrets (#216)
* Update db init code to verify that all the required environment variables are set. * Add a workaround for integration secret encryption code - make sure we don't try to encrypt the secret twice. This is needed due to the bug in the code which would encrypt secrets even if they are already encrypted. * fixes spelling & folder structure --------- Co-authored-by: Christian Beutel <>
This commit is contained in:
107
db/tests/secrets_test.go
Normal file
107
db/tests/secrets_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"pocketbase/util"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
func TestLooksLikeEncrypted(t *testing.T) {
|
||||
// Sub-test for non-base64 input.
|
||||
t.Run("NonBase64", func(t *testing.T) {
|
||||
if util.LooksLikeEncrypted("not a base64 string!") {
|
||||
t.Errorf("Expected false for non-base64 input")
|
||||
}
|
||||
})
|
||||
|
||||
// Sub-test for an empty string.
|
||||
t.Run("EmptyString", func(t *testing.T) {
|
||||
if util.LooksLikeEncrypted("") {
|
||||
t.Errorf("Expected false for empty string")
|
||||
}
|
||||
})
|
||||
|
||||
// Create a dummy byte slice to determine the nonce size using a dummy 32-byte key.
|
||||
dummyKey := make([]byte, 32)
|
||||
block, err := aes.NewCipher(dummyKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create dummy cipher: %v", err)
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create dummy GCM: %v", err)
|
||||
}
|
||||
nonceSize := gcm.NonceSize()
|
||||
minSize := nonceSize + 16
|
||||
|
||||
// Sub-test for data length less than nonce+16 bytes.
|
||||
t.Run("ShortData", func(t *testing.T) {
|
||||
shortData := make([]byte, minSize-1)
|
||||
encodedShort := base64.StdEncoding.EncodeToString(shortData)
|
||||
if util.LooksLikeEncrypted(encodedShort) {
|
||||
t.Errorf("Expected false for data length < nonce+16, got true")
|
||||
}
|
||||
})
|
||||
|
||||
// Sub-test for data length exactly equal to nonce+16 bytes.
|
||||
t.Run("ExactData", func(t *testing.T) {
|
||||
exactData := make([]byte, minSize)
|
||||
encodedExact := base64.StdEncoding.EncodeToString(exactData)
|
||||
if !util.LooksLikeEncrypted(encodedExact) {
|
||||
t.Errorf("Expected true for data length == nonce+16")
|
||||
}
|
||||
})
|
||||
|
||||
// Sub-test for data length greater than nonce+16 bytes.
|
||||
t.Run("LongData", func(t *testing.T) {
|
||||
longData := make([]byte, minSize+10)
|
||||
encodedLong := base64.StdEncoding.EncodeToString(longData)
|
||||
if !util.LooksLikeEncrypted(encodedLong) {
|
||||
t.Errorf("Expected true for data length > nonce+16")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCanDecryptSecret(t *testing.T) {
|
||||
// Sub-test: When POCKETBASE_ENCRYPTION_KEY is not set.
|
||||
t.Run("NoEncryptionKey", func(t *testing.T) {
|
||||
os.Unsetenv("POCKETBASE_ENCRYPTION_KEY")
|
||||
if util.CanDecryptSecret("anyciphertext") {
|
||||
t.Errorf("Expected false when POCKETBASE_ENCRYPTION_KEY is not set")
|
||||
}
|
||||
})
|
||||
|
||||
// Set a valid 32-byte key (for AES-256).
|
||||
encryptionKey := "0123456789abcdef0123456789abcdef" // exactly 32 bytes
|
||||
os.Setenv("POCKETBASE_ENCRYPTION_KEY", encryptionKey)
|
||||
// Cleanup environment variable after the test.
|
||||
t.Cleanup(func() {
|
||||
os.Unsetenv("POCKETBASE_ENCRYPTION_KEY")
|
||||
})
|
||||
|
||||
// Sub-test: Valid ciphertext should return true.
|
||||
t.Run("ValidCiphertext", func(t *testing.T) {
|
||||
plaintext := "my secret message"
|
||||
// Cast plaintext to []byte as required by security.Encrypt.
|
||||
ciphertext, err := security.Encrypt([]byte(plaintext), encryptionKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encrypt secret: %v", err)
|
||||
}
|
||||
if !util.CanDecryptSecret(ciphertext) {
|
||||
t.Errorf("Expected true for valid ciphertext with correct key")
|
||||
}
|
||||
})
|
||||
|
||||
// Sub-test: Invalid ciphertext should return false.
|
||||
t.Run("InvalidCiphertext", func(t *testing.T) {
|
||||
invalidCiphertext := "invalid_ciphertext"
|
||||
if util.CanDecryptSecret(invalidCiphertext) {
|
||||
t.Errorf("Expected false for invalid ciphertext")
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user