- Remove internal/macse package (Secure Enclave experiment) - Fix errcheck: handle keychain.DeleteItem error return - Fix lll: break long lines in command descriptions - Fix mnd: add nolint comment for cobra.ExactArgs(2) - Fix nlreturn: add blank lines before return/break statements - Fix revive: add nolint comment for KEYCHAIN_APP_IDENTIFIER constant - Fix nestif: simplify UnlockersRemove by using new NumSecrets method - Add NumSecrets() method to vault.Vault for counting secrets - Update golangci.yml to exclude ALL_CAPS warning (attempted various configurations but settled on nolint comment) All tests pass, code is formatted and linted.
168 lines
5.0 KiB
Go
168 lines
5.0 KiB
Go
//go:build darwin
|
|
// +build darwin
|
|
|
|
package secret
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/awnumar/memguard"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestKeychainStoreRetrieveDelete(t *testing.T) {
|
|
// Skip test if not on macOS
|
|
if runtime.GOOS != "darwin" {
|
|
t.Skip("Keychain tests only run on macOS")
|
|
}
|
|
|
|
// Test data
|
|
testItemName := "test-secret-keychain-item"
|
|
testData := "test-secret-data-12345"
|
|
testBuffer := memguard.NewBufferFromBytes([]byte(testData))
|
|
defer testBuffer.Destroy()
|
|
|
|
// Clean up any existing item first
|
|
_ = deleteFromKeychain(testItemName)
|
|
|
|
// Test 1: Store data in keychain
|
|
err := storeInKeychain(testItemName, testBuffer)
|
|
require.NoError(t, err, "Failed to store data in keychain")
|
|
|
|
// Test 2: Retrieve data from keychain
|
|
retrievedData, err := retrieveFromKeychain(testItemName)
|
|
require.NoError(t, err, "Failed to retrieve data from keychain")
|
|
assert.Equal(t, testData, string(retrievedData), "Retrieved data doesn't match stored data")
|
|
|
|
// Test 3: Update existing item (store again with different data)
|
|
newTestData := "updated-test-data-67890"
|
|
newTestBuffer := memguard.NewBufferFromBytes([]byte(newTestData))
|
|
defer newTestBuffer.Destroy()
|
|
|
|
err = storeInKeychain(testItemName, newTestBuffer)
|
|
require.NoError(t, err, "Failed to update data in keychain")
|
|
|
|
// Verify updated data
|
|
retrievedData, err = retrieveFromKeychain(testItemName)
|
|
require.NoError(t, err, "Failed to retrieve updated data from keychain")
|
|
assert.Equal(t, newTestData, string(retrievedData), "Retrieved data doesn't match updated data")
|
|
|
|
// Test 4: Delete from keychain
|
|
err = deleteFromKeychain(testItemName)
|
|
require.NoError(t, err, "Failed to delete data from keychain")
|
|
|
|
// Test 5: Verify item is deleted (should fail to retrieve)
|
|
_, err = retrieveFromKeychain(testItemName)
|
|
assert.Error(t, err, "Expected error when retrieving deleted item")
|
|
}
|
|
|
|
func TestKeychainInvalidItemName(t *testing.T) {
|
|
// Skip test if not on macOS
|
|
if runtime.GOOS != "darwin" {
|
|
t.Skip("Keychain tests only run on macOS")
|
|
}
|
|
|
|
testData := memguard.NewBufferFromBytes([]byte("test"))
|
|
defer testData.Destroy()
|
|
|
|
// Test invalid item names
|
|
invalidNames := []string{
|
|
"", // Empty name
|
|
"test space", // Contains space
|
|
"test/slash", // Contains slash
|
|
"test\\backslash", // Contains backslash
|
|
"test:colon", // Contains colon
|
|
"test;semicolon", // Contains semicolon
|
|
"test|pipe", // Contains pipe
|
|
"test@at", // Contains @
|
|
"test#hash", // Contains #
|
|
"test$dollar", // Contains $
|
|
"test&ersand", // Contains &
|
|
"test*asterisk", // Contains *
|
|
"test?question", // Contains ?
|
|
"test!exclamation", // Contains !
|
|
"test'quote", // Contains single quote
|
|
"test\"doublequote", // Contains double quote
|
|
"test(paren", // Contains parenthesis
|
|
"test[bracket", // Contains bracket
|
|
}
|
|
|
|
for _, name := range invalidNames {
|
|
err := storeInKeychain(name, testData)
|
|
assert.Error(t, err, "Expected error for invalid name: %s", name)
|
|
assert.Contains(t, err.Error(), "invalid keychain item name", "Error should mention invalid name for: %s", name)
|
|
}
|
|
|
|
// Test valid names (should not error on validation)
|
|
validNames := []string{
|
|
"test-name",
|
|
"test_name",
|
|
"test.name",
|
|
"TestName123",
|
|
"TEST_NAME_123",
|
|
"com.example.test",
|
|
"secret-vault-hostname-2024-01-01",
|
|
}
|
|
|
|
for _, name := range validNames {
|
|
err := validateKeychainItemName(name)
|
|
assert.NoError(t, err, "Expected no error for valid name: %s", name)
|
|
// Clean up
|
|
_ = deleteFromKeychain(name)
|
|
}
|
|
}
|
|
|
|
func TestKeychainNilData(t *testing.T) {
|
|
// Skip test if not on macOS
|
|
if runtime.GOOS != "darwin" {
|
|
t.Skip("Keychain tests only run on macOS")
|
|
}
|
|
|
|
// Test storing nil data
|
|
err := storeInKeychain("test-item", nil)
|
|
assert.Error(t, err, "Expected error when storing nil data")
|
|
assert.Contains(t, err.Error(), "data buffer is nil")
|
|
}
|
|
|
|
func TestKeychainLargeData(t *testing.T) {
|
|
// Skip test if not on macOS
|
|
if runtime.GOOS != "darwin" {
|
|
t.Skip("Keychain tests only run on macOS")
|
|
}
|
|
|
|
// Test with larger hex-encoded data (512 bytes of binary data = 1KB hex)
|
|
largeData := make([]byte, 512)
|
|
for i := range largeData {
|
|
largeData[i] = byte(i % 256)
|
|
}
|
|
|
|
// Convert to hex string for storage
|
|
hexData := hex.EncodeToString(largeData)
|
|
|
|
testItemName := "test-large-data"
|
|
testBuffer := memguard.NewBufferFromBytes([]byte(hexData))
|
|
defer testBuffer.Destroy()
|
|
|
|
// Clean up first
|
|
_ = deleteFromKeychain(testItemName)
|
|
|
|
// Store hex data
|
|
err := storeInKeychain(testItemName, testBuffer)
|
|
require.NoError(t, err, "Failed to store large data")
|
|
|
|
// Retrieve and verify
|
|
retrievedData, err := retrieveFromKeychain(testItemName)
|
|
require.NoError(t, err, "Failed to retrieve large data")
|
|
|
|
// Decode hex and compare
|
|
decodedData, err := hex.DecodeString(string(retrievedData))
|
|
require.NoError(t, err, "Failed to decode hex data")
|
|
assert.Equal(t, largeData, decodedData, "Large data mismatch")
|
|
|
|
// Clean up
|
|
_ = deleteFromKeychain(testItemName)
|
|
}
|