fix: resolve exported type stuttering issues (revive)

- Rename VaultMetadata to Metadata in internal/vault package to avoid stuttering
- Rename BIP85DRNG to DRNG in pkg/bip85 package to avoid stuttering
- Update all references in code and tests
This commit is contained in:
Jeffrey Paul 2025-06-20 12:47:06 -07:00
parent 4062242063
commit bdcddadf90
21 changed files with 89 additions and 34 deletions

View File

@ -15,7 +15,8 @@
"Bash(golangci-lint run:*)",
"Bash(git add:*)",
"Bash(gofumpt:*)",
"Bash(git stash:*)"
"Bash(git stash:*)",
"Bash(git commit:*)"
],
"deny": []
}

View File

@ -1283,6 +1283,7 @@ func test18AgeKeyOperations(t *testing.T, tempDir, secretPath, testMnemonic stri
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
}
output, err := cmd.CombinedOutput()
return string(output), err
}
@ -1349,6 +1350,7 @@ func test19DisasterRecovery(t *testing.T, tempDir, secretPath, testMnemonic stri
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
}
output, err := cmd.CombinedOutput()
return string(output), err
}
@ -1443,6 +1445,7 @@ func test20VersionTimestamps(t *testing.T, tempDir, secretPath, testMnemonic str
fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
}
output, err := cmd.CombinedOutput()
return string(output), err
}
@ -2076,6 +2079,7 @@ func readFile(t *testing.T, path string) []byte {
t.Helper()
data, err := os.ReadFile(path)
require.NoError(t, err, "Should be able to read file: %s", path)
return data
}
@ -2125,6 +2129,7 @@ func copyDir(src, dst string) error {
}
}
}
return nil
}

View File

@ -244,7 +244,7 @@ func (cli *Instance) VaultImport(cmd *cobra.Command, vaultName string) error {
existingMetadata, err := vault.LoadVaultMetadata(cli.fs, vaultDir)
if err != nil {
// If metadata doesn't exist, create new
existingMetadata = &vault.VaultMetadata{
existingMetadata = &vault.Metadata{
CreatedAt: time.Now(),
}
}

View File

@ -126,6 +126,7 @@ func (cli *Instance) ListVersions(cmd *cobra.Command, secretName string) error {
status = "current (error)"
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", version, "-", status, "-", "-")
continue
}
@ -192,5 +193,6 @@ func (cli *Instance) PromoteVersion(cmd *cobra.Command, secretName string, versi
}
cmd.Printf("Promoted version %s to current for secret '%s'\n", version, secretName)
return nil
}

View File

@ -145,6 +145,7 @@ func (k *KeychainUnlocker) GetID() string {
// We cannot continue with a fallback ID as that would mask data corruption
panic(fmt.Sprintf("Keychain unlocker metadata is corrupt or missing keychain item name: %v", err))
}
return fmt.Sprintf("%s-keychain", keychainItemName)
}
@ -172,6 +173,7 @@ func (k *KeychainUnlocker) Remove() error {
}
Debug("Successfully removed keychain unlocker", "unlocker_id", k.GetID(), "keychain_item", keychainItemName)
return nil
}
@ -210,6 +212,7 @@ func generateKeychainUnlockerName(vaultName string) (string, error) {
// Format: secret-<vault>-<hostname>-<date>
enrollmentDate := time.Now().Format("2006-01-02")
return fmt.Sprintf("secret-%s-%s-%s", vaultName, hostname, enrollmentDate), nil
}
@ -377,7 +380,9 @@ func CreateKeychainUnlocker(fs afero.Fs, stateDir string) (*KeychainUnlocker, er
return nil, fmt.Errorf("failed to marshal unlocker metadata: %w", err)
}
if err := afero.WriteFile(fs, filepath.Join(unlockerDir, "unlocker-metadata.json"), metadataBytes, FilePerms); err != nil {
if err := afero.WriteFile(fs,
filepath.Join(unlockerDir, "unlocker-metadata.json"),
metadataBytes, FilePerms); err != nil {
return nil, fmt.Errorf("failed to write unlocker metadata: %w", err)
}
@ -394,6 +399,7 @@ func checkMacOSAvailable() error {
if err := cmd.Run(); err != nil {
return fmt.Errorf("macOS security command not available: %w (keychain unlockers are only supported on macOS)", err)
}
return nil
}
@ -415,7 +421,7 @@ func storeInKeychain(itemName string, data []byte) error {
if err := validateKeychainItemName(itemName); err != nil {
return fmt.Errorf("invalid keychain item name: %w", err)
}
cmd := exec.Command("/usr/bin/security", "add-generic-password", //nolint:gosec // Input validated by validateKeychainItemName
cmd := exec.Command("/usr/bin/security", "add-generic-password", //nolint:gosec
"-a", itemName,
"-s", itemName,
"-w", string(data),
@ -434,7 +440,7 @@ func retrieveFromKeychain(itemName string) ([]byte, error) {
return nil, fmt.Errorf("invalid keychain item name: %w", err)
}
cmd := exec.Command("/usr/bin/security", "find-generic-password", //nolint:gosec // Input validated by validateKeychainItemName
cmd := exec.Command("/usr/bin/security", "find-generic-password", //nolint:gosec
"-a", itemName,
"-s", itemName,
"-w") // Return password only
@ -458,7 +464,7 @@ func deleteFromKeychain(itemName string) error {
return fmt.Errorf("invalid keychain item name: %w", err)
}
cmd := exec.Command("/usr/bin/security", "delete-generic-password", //nolint:gosec // Input validated by validateKeychainItemName
cmd := exec.Command("/usr/bin/security", "delete-generic-password", //nolint:gosec
"-a", itemName,
"-s", itemName)

View File

@ -9,8 +9,10 @@ type VaultMetadata struct {
CreatedAt time.Time `json:"createdAt"`
Description string `json:"description,omitempty"`
DerivationIndex uint32 `json:"derivation_index"`
PublicKeyHash string `json:"public_key_hash,omitempty"` // Double SHA256 hash of the actual long-term public key
MnemonicFamilyHash string `json:"mnemonic_family_hash,omitempty"` // Double SHA256 hash of index-0 key (for grouping vaults from same mnemonic)
// Double SHA256 hash of the actual long-term public key
PublicKeyHash string `json:"public_key_hash,omitempty"`
// Double SHA256 hash of index-0 key (for grouping vaults from same mnemonic)
MnemonicFamilyHash string `json:"mnemonic_family_hash,omitempty"`
}
// UnlockerMetadata contains information about an unlocker

View File

@ -121,6 +121,7 @@ func (p *PassphraseUnlocker) Remove() error {
if err := p.fs.RemoveAll(p.Directory); err != nil {
return fmt.Errorf("failed to remove passphrase unlocker directory: %w", err)
}
return nil
}

View File

@ -129,6 +129,7 @@ func (p *PGPUnlocker) GetID() string {
// We cannot continue with a fallback ID as that would mask data corruption
panic(fmt.Sprintf("PGP unlocker metadata is corrupt or missing GPG key ID: %v", err))
}
return fmt.Sprintf("%s-pgp", gpgKeyID)
}
@ -139,6 +140,7 @@ func (p *PGPUnlocker) Remove() error {
if err := p.fs.RemoveAll(p.Directory); err != nil {
return fmt.Errorf("failed to remove PGP unlocker directory: %w", err)
}
return nil
}
@ -177,6 +179,7 @@ func generatePGPUnlockerName() (string, error) {
// Format: hostname-pgp-YYYY-MM-DD
enrollmentDate := time.Now().Format("2006-01-02")
return fmt.Sprintf("%s-pgp-%s", hostname, enrollmentDate), nil
}
@ -320,7 +323,9 @@ func CreatePGPUnlocker(fs afero.Fs, stateDir string, gpgKeyID string) (*PGPUnloc
return nil, fmt.Errorf("failed to marshal unlocker metadata: %w", err)
}
if err := afero.WriteFile(fs, filepath.Join(unlockerDir, "unlocker-metadata.json"), metadataBytes, FilePerms); err != nil {
if err := afero.WriteFile(fs,
filepath.Join(unlockerDir, "unlocker-metadata.json"),
metadataBytes, FilePerms); err != nil {
return nil, fmt.Errorf("failed to write unlocker metadata: %w", err)
}
@ -377,6 +382,7 @@ func checkGPGAvailable() error {
if err := cmd.Run(); err != nil {
return fmt.Errorf("GPG not available: %w (make sure 'gpg' command is installed and in PATH)", err)
}
return nil
}

View File

@ -78,6 +78,7 @@ func (s *Secret) Save(value []byte, force bool) error {
}
Debug("Successfully saved secret", "secret_name", s.Name)
return nil
}
@ -220,6 +221,7 @@ func (s *Secret) LoadMetadata() error {
CreatedAt: now,
UpdatedAt: now,
}
return nil
}
@ -278,6 +280,7 @@ func GetCurrentVault(fs afero.Fs, stateDir string) (VaultInterface, error) {
if getCurrentVaultFunc == nil {
return nil, fmt.Errorf("GetCurrentVault function not registered")
}
return getCurrentVaultFunc(fs, stateDir)
}

View File

@ -262,6 +262,7 @@ func isValidSecretName(name string) bool {
return false
}
}
return true
}

View File

@ -51,6 +51,7 @@ func NewVersion(vault VaultInterface, secretName string, version string) *Versio
)
now := time.Now()
return &Version{
SecretName: secretName,
Version: version,
@ -219,6 +220,7 @@ func (sv *Version) Save(value []byte) error {
}
Debug("Successfully saved secret version", "version", sv.Version, "secret_name", sv.SecretName)
return nil
}
@ -277,6 +279,7 @@ func (sv *Version) LoadMetadata(ltIdentity *age.X25519Identity) error {
sv.Metadata = metadata
Debug("Successfully loaded version metadata", "version", sv.Version)
return nil
}
@ -344,6 +347,7 @@ func (sv *Version) GetValue(ltIdentity *age.X25519Identity) ([]byte, error) {
"version", sv.Version,
"value_length", len(value),
"is_empty", len(value) == 0)
return value, nil
}
@ -392,6 +396,7 @@ func GetCurrentVersion(fs afero.Fs, secretDir string) (string, error) {
if len(parts) >= 2 && parts[0] == "versions" {
return parts[1], nil
}
return "", fmt.Errorf("invalid current version symlink format: %s", target)
}
}

View File

@ -40,7 +40,9 @@ func TestVersionIntegrationWorkflow(t *testing.T) {
stateDir := "/test/state"
// Set mnemonic for testing
t.Setenv(secret.EnvMnemonic, "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")
t.Setenv(secret.EnvMnemonic,
"abandon abandon abandon abandon abandon abandon "+
"abandon abandon abandon abandon abandon about")
// Create vault
vault, err := CreateVault(fs, stateDir, "test")
@ -278,7 +280,7 @@ func TestVersionConcurrency(t *testing.T) {
done := make(chan bool, 10)
errors := make(chan error, 10)
for i := 0; i < 10; i++ {
for range 10 {
go func() {
value, err := vault.GetSecret(secretName)
if err != nil {
@ -291,7 +293,7 @@ func TestVersionConcurrency(t *testing.T) {
}
// Wait for all goroutines
for i := 0; i < 10; i++ {
for range 10 {
<-done
}

View File

@ -26,6 +26,7 @@ func isValidVaultName(name string) bool {
return false
}
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_]+$`, name)
return matched
}
@ -85,6 +86,7 @@ func ResolveVaultSymlink(fs afero.Fs, symlinkPath string) (string, error) {
}
secret.Debug("resolveVaultSymlink completed successfully", "result", target)
return target, nil
}
}
@ -102,6 +104,7 @@ func ResolveVaultSymlink(fs afero.Fs, symlinkPath string) (string, error) {
secret.Debug("Read target path from file", "target", target)
secret.Debug("resolveVaultSymlink completed via fallback", "result", target)
return target, nil
}
@ -250,7 +253,7 @@ func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) {
}
// Save vault metadata
metadata := &VaultMetadata{
metadata := &Metadata{
CreatedAt: time.Now(),
DerivationIndex: derivationIndex,
PublicKeyHash: publicKeyHash,
@ -268,6 +271,7 @@ func CreateVault(fs afero.Fs, stateDir string, name string) (*Vault, error) {
// Create and return the vault
secret.Debug("Successfully created vault", "name", name)
return NewVault(fs, stateDir, name), nil
}
@ -321,5 +325,6 @@ func SelectVault(fs afero.Fs, stateDir string, name string) error {
}
secret.Debug("Successfully selected vault", "vault_name", name)
return nil
}

View File

@ -14,7 +14,7 @@ import (
// Alias the metadata types from secret package for convenience
type (
VaultMetadata = secret.VaultMetadata
Metadata = secret.VaultMetadata
UnlockerMetadata = secret.UnlockerMetadata
SecretMetadata = secret.Metadata
Configuration = secret.Configuration
@ -24,6 +24,7 @@ type (
func ComputeDoubleSHA256(data []byte) string {
firstHash := sha256.Sum256(data)
secondHash := sha256.Sum256(firstHash[:])
return hex.EncodeToString(secondHash[:])
}
@ -71,7 +72,7 @@ func GetNextDerivationIndex(fs afero.Fs, stateDir string, mnemonic string) (uint
continue
}
var metadata VaultMetadata
var metadata Metadata
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
// Skip vaults with invalid metadata
continue
@ -93,7 +94,7 @@ func GetNextDerivationIndex(fs afero.Fs, stateDir string, mnemonic string) (uint
}
// SaveVaultMetadata saves vault metadata to the vault directory
func SaveVaultMetadata(fs afero.Fs, vaultDir string, metadata *VaultMetadata) error {
func SaveVaultMetadata(fs afero.Fs, vaultDir string, metadata *Metadata) error {
metadataPath := filepath.Join(vaultDir, "vault-metadata.json")
metadataBytes, err := json.MarshalIndent(metadata, "", " ")
@ -109,7 +110,7 @@ func SaveVaultMetadata(fs afero.Fs, vaultDir string, metadata *VaultMetadata) er
}
// LoadVaultMetadata loads vault metadata from the vault directory
func LoadVaultMetadata(fs afero.Fs, vaultDir string) (*VaultMetadata, error) {
func LoadVaultMetadata(fs afero.Fs, vaultDir string) (*Metadata, error) {
metadataPath := filepath.Join(vaultDir, "vault-metadata.json")
metadataBytes, err := afero.ReadFile(fs, metadataPath)
@ -117,7 +118,7 @@ func LoadVaultMetadata(fs afero.Fs, vaultDir string) (*VaultMetadata, error) {
return nil, fmt.Errorf("failed to read vault metadata: %w", err)
}
var metadata VaultMetadata
var metadata Metadata
if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
return nil, fmt.Errorf("failed to unmarshal vault metadata: %w", err)
}

View File

@ -68,7 +68,7 @@ func TestVaultMetadata(t *testing.T) {
t.Fatalf("Failed to write public key: %v", err)
}
metadata1 := &VaultMetadata{
metadata1 := &Metadata{
DerivationIndex: 0,
PublicKeyHash: pubKeyHash0, // Hash of the actual key (index 0)
MnemonicFamilyHash: pubKeyHash0, // Hash of index 0 key (for family identification)
@ -117,7 +117,7 @@ func TestVaultMetadata(t *testing.T) {
// Compute the hash for index 5 key
pubKeyHash5 := ComputeDoubleSHA256([]byte(pubKey5))
metadata2 := &VaultMetadata{
metadata2 := &Metadata{
DerivationIndex: 5,
PublicKeyHash: pubKeyHash5, // Hash of the actual key (index 5)
MnemonicFamilyHash: pubKeyHash0, // Same family hash since it's from the same mnemonic
@ -143,7 +143,7 @@ func TestVaultMetadata(t *testing.T) {
}
// Create and save metadata
metadata := &VaultMetadata{
metadata := &Metadata{
DerivationIndex: 3,
PublicKeyHash: "test-public-key-hash",
}

View File

@ -89,6 +89,7 @@ func isValidSecretName(name string) bool {
// Check the basic pattern
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_\/]+$`, name)
return matched
}
@ -221,7 +222,10 @@ func (v *Vault) AddSecret(name string, value []byte, force bool) error {
return fmt.Errorf("failed to set current version: %w", err)
}
secret.Debug("Successfully added secret version to vault", "secret_name", name, "version", versionName, "vault_name", v.Name)
secret.Debug("Successfully added secret version to vault",
"secret_name", name, "version", versionName,
"vault_name", v.Name)
return nil
}

View File

@ -39,7 +39,8 @@ func (v *Vault) GetCurrentUnlocker() (secret.Unlocker, error) {
// Try to read as symlink first
unlockerDir, err = linkReader.ReadlinkIfPossible(currentUnlockerPath)
if err != nil {
secret.Debug("Failed to read symlink, falling back to file contents", "error", err, "symlink_path", currentUnlockerPath)
secret.Debug("Failed to read symlink, falling back to file contents",
"error", err, "symlink_path", currentUnlockerPath)
// Fallback: read the path from file contents
unlockerDirBytes, err := afero.ReadFile(v.fs, currentUnlockerPath)
if err != nil {
@ -363,7 +364,9 @@ func (v *Vault) CreatePassphraseUnlocker(passphrase string) (*secret.PassphraseU
// Write public key
pubKeyPath := filepath.Join(unlockerDir, "pub.age")
if err := afero.WriteFile(v.fs, pubKeyPath, []byte(unlockerIdentity.Recipient().String()), secret.FilePerms); err != nil {
if err := afero.WriteFile(v.fs, pubKeyPath,
[]byte(unlockerIdentity.Recipient().String()),
secret.FilePerms); err != nil {
return nil, fmt.Errorf("failed to write unlocker public key: %w", err)
}

View File

@ -30,6 +30,7 @@ func NewVault(fs afero.Fs, stateDir string, name string) *Vault {
longTermKey: nil,
}
secret.Debug("Created NewVault instance successfully")
return v
}
@ -92,6 +93,7 @@ func (v *Vault) GetOrDeriveLongTermKey() (*age.X25519Identity, error) {
"derived_hash", derivedPubKeyHash,
"stored_hash", metadata.PublicKeyHash,
"derivation_index", metadata.DerivationIndex)
return nil, fmt.Errorf("derived public key does not match vault: mnemonic may be incorrect")
}
@ -178,7 +180,8 @@ func (v *Vault) GetOrDeriveLongTermKey() (*age.X25519Identity, error) {
// Cache the derived key by unlocking the vault
v.Unlock(ltIdentity)
secret.Debug("Vault is unlocked (lt key in memory) via unlocker", "vault_name", v.Name, "unlocker_type", unlocker.GetType())
secret.Debug("Vault is unlocked (lt key in memory) via unlocker",
"vault_name", v.Name, "unlocker_type", unlocker.GetType())
return ltIdentity, nil
}

View File

@ -54,6 +54,7 @@ func IdentityFromEntropy(ent []byte) (*age.X25519Identity, error) {
if err != nil {
return nil, fmt.Errorf("bech32 encode: %w", err)
}
return age.ParseX25519Identity(strings.ToUpper(s))
}
@ -125,6 +126,7 @@ func DeriveIdentity(mnemonic string, n uint32) (*age.X25519Identity, error) {
if err != nil {
return nil, err
}
return IdentityFromEntropy(ent)
}
@ -135,5 +137,6 @@ func DeriveIdentityFromXPRV(xprv string, n uint32) (*age.X25519Identity, error)
if err != nil {
return nil, err
}
return IdentityFromEntropy(ent)
}

View File

@ -393,6 +393,7 @@ func TestIdentityFromEntropyEdgeCases(t *testing.T) {
err,
) // In test context, panic is acceptable for setup failures
}
return b
}(),
expectError: false,

View File

@ -45,29 +45,29 @@ var (
TestNetPrivateKey = []byte{0x04, 0x35, 0x83, 0x94}
)
// BIP85DRNG is a deterministic random number generator seeded by BIP85 entropy
type BIP85DRNG struct {
// DRNG is a deterministic random number generator seeded by BIP85 entropy
type DRNG struct {
shake io.Reader
}
// NewBIP85DRNG creates a new DRNG seeded with BIP85 entropy
func NewBIP85DRNG(entropy []byte) *BIP85DRNG {
func NewBIP85DRNG(entropy []byte) *DRNG {
// The entropy must be exactly 64 bytes (512 bits)
if len(entropy) != 64 {
panic("BIP85DRNG entropy must be 64 bytes")
panic("DRNG entropy must be 64 bytes")
}
// Initialize SHAKE256 with the entropy
shake := sha3.NewShake256()
shake.Write(entropy)
return &BIP85DRNG{
return &DRNG{
shake: shake,
}
}
// Read implements the io.Reader interface
func (d *BIP85DRNG) Read(p []byte) (n int, err error) {
func (d *DRNG) Read(p []byte) (n int, err error) {
return d.shake.Read(p)
}
@ -266,6 +266,7 @@ func DeriveXPRV(masterKey *hdkeychain.ExtendedKey, index uint32) (*hdkeychain.Ex
func doubleSHA256(data []byte) []byte {
hash1 := sha256.Sum256(data)
hash2 := sha256.Sum256(hash1[:])
return hash2[:]
}