Stop vault safety checks from reading unreadable state as empty (closes #51)
check / check (push) Waiting to run
check / check (push) Waiting to run
Adding a PGP unlocker checked unlockers.d for a duplicate and, when the directory could not be read, reported no duplicate and went on. It now stops with an error naming the directory and cause. The same flaw guarded removing the last unlocker and removing a vault (an unreadable secrets directory counted as no secrets) and vault import (an unreadable pub.age counted as no long-term key). Those now stop with an error too. `unlocker list` keeps skipping entries it cannot read. `vault rm` and `unlocker rm` now take the state directory lock and call an unexported function that does the work, as `vault import` does, so the tests can reach their checks. Model: opus-5-5
This commit is contained in:
+30
-8
@@ -400,8 +400,12 @@ func (cli *Instance) vaultImportPreflight(
|
||||
// Check if vault already has a public key
|
||||
pubKeyPath := vaultDir + "/pub.age"
|
||||
|
||||
_, err = cli.fs.Stat(pubKeyPath)
|
||||
if err == nil {
|
||||
exists, err = afero.Exists(cli.fs, pubKeyPath)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to check %s: %w", pubKeyPath, err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return "", "", "", fmt.Errorf("vault '%s' %w",
|
||||
vaultName, errVaultHasLongTermKey)
|
||||
}
|
||||
@@ -561,17 +565,26 @@ func (cli *Instance) importMnemonic(cmd *cobra.Command, vaultName string) error
|
||||
}
|
||||
|
||||
// vaultHasSecrets reports whether the vault directory contains any secrets
|
||||
func (cli *Instance) vaultHasSecrets(vaultDir string) bool {
|
||||
func (cli *Instance) vaultHasSecrets(vaultDir string) (bool, error) {
|
||||
secretsDir := filepath.Join(vaultDir, "secrets.d")
|
||||
|
||||
exists, _ := afero.DirExists(cli.fs, secretsDir)
|
||||
exists, err := afero.DirExists(cli.fs, secretsDir)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check secrets directory %s: %w",
|
||||
secretsDir, err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
entries, err := afero.ReadDir(cli.fs, secretsDir)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to read secrets directory %s: %w",
|
||||
secretsDir, err)
|
||||
}
|
||||
|
||||
return err == nil && len(entries) > 0
|
||||
return len(entries) > 0, nil
|
||||
}
|
||||
|
||||
// switchAwayFromVault selects another vault as current before removal
|
||||
@@ -600,7 +613,8 @@ func (cli *Instance) switchAwayFromVault(
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveVault removes a vault with safety checks
|
||||
// RemoveVault removes a vault, holding the state directory lock while
|
||||
// removeVault runs
|
||||
func (cli *Instance) RemoveVault(cmd *cobra.Command, name string, force bool) error {
|
||||
release, err := vault.LockStateDir(cli.fs, cli.stateDir)
|
||||
if err != nil {
|
||||
@@ -608,6 +622,11 @@ func (cli *Instance) RemoveVault(cmd *cobra.Command, name string, force bool) er
|
||||
}
|
||||
defer release()
|
||||
|
||||
return cli.removeVault(cmd, name, force)
|
||||
}
|
||||
|
||||
// removeVault removes a vault with safety checks
|
||||
func (cli *Instance) removeVault(cmd *cobra.Command, name string, force bool) error {
|
||||
// Get list of all vaults
|
||||
vaults, err := vault.ListVaults(cli.fs, cli.stateDir)
|
||||
if err != nil {
|
||||
@@ -641,7 +660,10 @@ func (cli *Instance) RemoveVault(cmd *cobra.Command, name string, force bool) er
|
||||
}
|
||||
|
||||
// Check if vault has secrets
|
||||
hasSecrets := cli.vaultHasSecrets(vaultDir)
|
||||
hasSecrets, err := cli.vaultHasSecrets(vaultDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Require --force if vault has secrets
|
||||
if hasSecrets && !force {
|
||||
|
||||
Reference in New Issue
Block a user