Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0b02b3dcb |
@@ -25,36 +25,22 @@ https://git.eeqj.de/sneak/secret/milestone/12
|
|||||||
`vault import`, `vault remove` and `version list`, `promote` and `rm` wrap
|
`vault import`, `vault remove` and `version list`, `promote` and `rm` wrap
|
||||||
the `vault` errors. `errUnsupportedUnlockerType` is removed: `secret
|
the `vault` errors. `errUnsupportedUnlockerType` is removed: `secret
|
||||||
unlocker add` gives `errInvalidUnlockerType` for an unknown type, whichever
|
unlocker add` gives `errInvalidUnlockerType` for an unknown type, whichever
|
||||||
check rejects it. Off macOS, adding a keychain or Secure Enclave unlocker
|
check rejects it. Messages are unchanged, except that `secret decrypt`
|
||||||
returns the `secret` package's error for it, not an `internal/cli` copy; on
|
of a missing secret says "not found", as `secret get` does, not "does not
|
||||||
macOS, the check that the system is macOS is gone, as it could never fail.
|
exist". Every error of `secret.ReadPassphrase` wraps
|
||||||
`secret vault import` gives `errInvalidMnemonicPhrase` for an invalid
|
`secret.ErrPassphraseNotRead`, which supplies the words "failed to read
|
||||||
mnemonic, as `init` and `vault create` do. `secret generate secret` gives
|
passphrase" that its callers used to add themselves; so two passphrases
|
||||||
`errLengthTooSmall` for a length below 1 wherever it is checked, and
|
that differ now give only "passphrases do not match", the words now follow
|
||||||
`errUnsupportedSecretType` for `--type mnemonic` too. `secret import` of a
|
"failed to read mnemonic:" and "failed to read passphrase confirmation:",
|
||||||
file over 100MB wraps `errSecretTooLarge`, as `secret add` returns it.
|
and a terminal read error no longer repeats them. A GPG key the keyring
|
||||||
`vault.ErrNilValueBuffer` is replaced by `secret.ErrNilValueBuffer`, which
|
does not hold gives `secret.ErrGPGKeyNotFound`, found by gpg's status line
|
||||||
`secret` already returned under another name. Messages are unchanged,
|
for "No public key"; before, the message repeated "failed to resolve GPG
|
||||||
except that `secret decrypt` of a missing secret says "not found", as
|
key fingerprint" and ended in gpg's exit status. The keychain unlocker
|
||||||
`secret get` does, not "does not exist"; `vault import` of an invalid
|
returns `errNilDataBuffer` for nil data; this and its test build only on
|
||||||
mnemonic says "invalid BIP39 mnemonic phrase"; `--type mnemonic` says
|
macOS with cgo and were only read. `bip85.ErrPasswordTooShort` and
|
||||||
"unsupported type: mnemonic (use 'secret generate mnemonic' instead)"; and
|
`ErrEncodedTooShort` are removed with their checks: 64 bytes of entropy
|
||||||
a file too large to import says
|
always give 86 Base64 or 80 Base85 characters, the most a password length
|
||||||
`failed to read secret from file <path>: secret too large: exceeds 100MB limit`.
|
may ask for. Tests that matched these errors' text use `errors.Is`.
|
||||||
Every error of `secret.ReadPassphrase` wraps `secret.ErrPassphraseNotRead`,
|
|
||||||
which supplies the words "failed to read passphrase" that its callers used
|
|
||||||
to add themselves; so two passphrases that differ now give only
|
|
||||||
"passphrases do not match", the words now follow "failed to read mnemonic:"
|
|
||||||
and "failed to read passphrase confirmation:", and a terminal read error no
|
|
||||||
longer repeats them. A GPG key the keyring does not hold gives
|
|
||||||
`secret.ErrGPGKeyNotFound`, found by gpg's status line for "No public key";
|
|
||||||
before, the message repeated "failed to resolve GPG key fingerprint" and
|
|
||||||
ended in gpg's exit status. The keychain unlocker returns `errNilDataBuffer`
|
|
||||||
for nil data; this and its test build only on macOS with cgo and were only
|
|
||||||
read. `bip85.ErrPasswordTooShort` and `ErrEncodedTooShort` are removed with
|
|
||||||
their checks: 64 bytes of entropy always give 86 Base64 or 80 Base85
|
|
||||||
characters, the most a password length may ask for. Tests that matched
|
|
||||||
these errors' text use `errors.Is`.
|
|
||||||
- 2026-10-04: Tests check which error a failure returns with `errors.Is`,
|
- 2026-10-04: Tests check which error a failure returns with `errors.Is`,
|
||||||
not by matching words of its message
|
not by matching words of its message
|
||||||
(https://git.eeqj.de/sneak/secret/issues/49). Every exported error that
|
(https://git.eeqj.de/sneak/secret/issues/49). Every exported error that
|
||||||
|
|||||||
@@ -20,7 +20,11 @@ const (
|
|||||||
|
|
||||||
// Sentinel errors for secret generation
|
// Sentinel errors for secret generation
|
||||||
var (
|
var (
|
||||||
errLengthTooSmall = errors.New("length must be at least 1")
|
errLengthTooSmall = errors.New("length must be at least 1")
|
||||||
|
errLengthNotPositive = errors.New("length must be positive")
|
||||||
|
errMnemonicTypeNotSupported = errors.New(
|
||||||
|
"mnemonic type not supported for secret generation, " +
|
||||||
|
"use 'secret generate mnemonic' instead")
|
||||||
errUnsupportedSecretType = errors.New("unsupported type")
|
errUnsupportedSecretType = errors.New("unsupported type")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -144,8 +148,7 @@ func (cli *Instance) GenerateSecret(
|
|||||||
case "alnum":
|
case "alnum":
|
||||||
secretValue, err = generateRandomAlnum(length)
|
secretValue, err = generateRandomAlnum(length)
|
||||||
case "mnemonic":
|
case "mnemonic":
|
||||||
return fmt.Errorf("%w: mnemonic (use 'secret generate mnemonic' instead)",
|
return errMnemonicTypeNotSupported
|
||||||
errUnsupportedSecretType)
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("%w: %s (supported: base58, alnum)",
|
return fmt.Errorf("%w: %s (supported: base58, alnum)",
|
||||||
errUnsupportedSecretType, secretType)
|
errUnsupportedSecretType, secretType)
|
||||||
@@ -201,8 +204,8 @@ func generateRandomAlnum(length int) (string, error) {
|
|||||||
// generateRandomString generates a random string of the specified length
|
// generateRandomString generates a random string of the specified length
|
||||||
// using the given character set
|
// using the given character set
|
||||||
func generateRandomString(length int, charset string) (string, error) {
|
func generateRandomString(length int, charset string) (string, error) {
|
||||||
if length < 1 {
|
if length <= 0 {
|
||||||
return "", errLengthTooSmall
|
return "", errLengthNotPositive
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]byte, length)
|
result := make([]byte, length)
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
//nolint:testpackage // white-box test of unexported internals
|
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.eeqj.de/sneak/secret/internal/vault"
|
|
||||||
"github.com/awnumar/memguard"
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestInvalidMnemonicError checks that every command that takes a mnemonic
|
|
||||||
// returns errInvalidMnemonicPhrase for one that is not valid BIP39. The vault
|
|
||||||
// "other" has no long-term key, as vault import needs.
|
|
||||||
func TestInvalidMnemonicError(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
command string
|
|
||||||
run func(c *Instance) error
|
|
||||||
}{
|
|
||||||
{"secret init", func(c *Instance) error { return c.Init(c.cmd) }},
|
|
||||||
{"secret vault create work", func(c *Instance) error {
|
|
||||||
return c.CreateVault(c.cmd, "work")
|
|
||||||
}},
|
|
||||||
{"secret vault import other", func(c *Instance) error {
|
|
||||||
return c.VaultImport(c.cmd, "other")
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.command, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
_, err := vault.CreateVault(fs, listTestStateDir, "other", nil, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
instance, _ := newTestInstance(fs)
|
|
||||||
instance.Mnemonic = memguard.NewBufferFromBytes([]byte("not a mnemonic"))
|
|
||||||
t.Cleanup(instance.Mnemonic.Destroy)
|
|
||||||
|
|
||||||
require.ErrorIs(t, tt.run(instance), errInvalidMnemonicPhrase)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGenerateSecretErrors checks that `secret generate secret` gives one
|
|
||||||
// error for a length below 1 and one for a type it cannot generate.
|
|
||||||
func TestGenerateSecretErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
instance, cmd := newTestInstance(afero.NewMemMapFs())
|
|
||||||
|
|
||||||
err := instance.GenerateSecret(cmd, "x", 0, "base58", false)
|
|
||||||
require.ErrorIs(t, err, errLengthTooSmall)
|
|
||||||
|
|
||||||
_, err = generateRandomString(0, "ab")
|
|
||||||
require.ErrorIs(t, err, errLengthTooSmall)
|
|
||||||
|
|
||||||
err = instance.GenerateSecret(cmd, "x", defaultSecretLength, "mnemonic", false)
|
|
||||||
require.ErrorIs(t, err, errUnsupportedSecretType)
|
|
||||||
|
|
||||||
err = instance.GenerateSecret(cmd, "x", defaultSecretLength, "hex", false)
|
|
||||||
require.ErrorIs(t, err, errUnsupportedSecretType)
|
|
||||||
}
|
|
||||||
@@ -32,7 +32,9 @@ const (
|
|||||||
|
|
||||||
// Sentinel errors for secret operations
|
// Sentinel errors for secret operations
|
||||||
var (
|
var (
|
||||||
errSecretTooLarge = errors.New("secret too large: exceeds 100MB limit")
|
errSecretTooLarge = errors.New("secret too large: exceeds 100MB limit")
|
||||||
|
errSecretFileTooLarge = errors.New(
|
||||||
|
"secret file too large: exceeds 100MB limit")
|
||||||
errCrossVaultSourceUnqualified = errors.New(
|
errCrossVaultSourceUnqualified = errors.New(
|
||||||
"source must specify vault (e.g., vault:secret) for cross-vault move")
|
"source must specify vault (e.g., vault:secret) for cross-vault move")
|
||||||
errMoveOntoItself = errors.New("cannot be moved onto itself")
|
errMoveOntoItself = errors.New("cannot be moved onto itself")
|
||||||
@@ -667,6 +669,10 @@ func (cli *Instance) ImportSecret(
|
|||||||
|
|
||||||
buffers, totalSize, err := readSecretFromReader(file)
|
buffers, totalSize, err := readSecretFromReader(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, errSecretTooLarge) {
|
||||||
|
return errSecretFileTooLarge
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Errorf("failed to read secret from file %s: %w", sourceFile, err)
|
return fmt.Errorf("failed to read secret from file %s: %w", sourceFile, err)
|
||||||
}
|
}
|
||||||
defer destroyBuffers(buffers)
|
defer destroyBuffers(buffers)
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ func TestImportSecretVariousSizes(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "101MB file - should fail",
|
name: "101MB file - should fail",
|
||||||
size: 101 * 1024 * 1024,
|
size: 101 * 1024 * 1024,
|
||||||
wantErr: errSecretTooLarge,
|
wantErr: errSecretFileTooLarge,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ var (
|
|||||||
errInvalidUnlockerType = errors.New("invalid unlocker type")
|
errInvalidUnlockerType = errors.New("invalid unlocker type")
|
||||||
errKeyIDOnlyForPGP = errors.New(
|
errKeyIDOnlyForPGP = errors.New(
|
||||||
"--keyid flag is only valid for PGP unlockers")
|
"--keyid flag is only valid for PGP unlockers")
|
||||||
|
errKeychainMacOSOnly = errors.New(
|
||||||
|
"keychain unlockers are only supported on macOS")
|
||||||
|
errSecureEnclaveMacOSOnly = errors.New(
|
||||||
|
"secure enclave unlockers are only supported on macOS")
|
||||||
// errGPGKeyAlreadyUnlocker carries only the message tail; the caller
|
// errGPGKeyAlreadyUnlocker carries only the message tail; the caller
|
||||||
// composes "GPG key <id> is already added as an unlocker".
|
// composes "GPG key <id> is already added as an unlocker".
|
||||||
errGPGKeyAlreadyUnlocker = errors.New(
|
errGPGKeyAlreadyUnlocker = errors.New(
|
||||||
@@ -489,6 +493,10 @@ func (cli *Instance) addPassphraseUnlocker(cmd *cobra.Command) error {
|
|||||||
|
|
||||||
// addKeychainUnlocker creates a macOS Keychain unlocker in the current vault
|
// addKeychainUnlocker creates a macOS Keychain unlocker in the current vault
|
||||||
func (cli *Instance) addKeychainUnlocker(cmd *cobra.Command) error {
|
func (cli *Instance) addKeychainUnlocker(cmd *cobra.Command) error {
|
||||||
|
if runtime.GOOS != platformDarwin {
|
||||||
|
return errKeychainMacOSOnly
|
||||||
|
}
|
||||||
|
|
||||||
keychainUnlocker, err := secret.CreateKeychainUnlocker(
|
keychainUnlocker, err := secret.CreateKeychainUnlocker(
|
||||||
cli.fs, cli.stateDir, cli.Mnemonic, cli.UnlockPassphrase)
|
cli.fs, cli.stateDir, cli.Mnemonic, cli.UnlockPassphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -516,6 +524,10 @@ func (cli *Instance) addKeychainUnlocker(cmd *cobra.Command) error {
|
|||||||
// addSecureEnclaveUnlocker creates a Secure Enclave unlocker in the
|
// addSecureEnclaveUnlocker creates a Secure Enclave unlocker in the
|
||||||
// current vault
|
// current vault
|
||||||
func (cli *Instance) addSecureEnclaveUnlocker(cmd *cobra.Command) error {
|
func (cli *Instance) addSecureEnclaveUnlocker(cmd *cobra.Command) error {
|
||||||
|
if runtime.GOOS != platformDarwin {
|
||||||
|
return errSecureEnclaveMacOSOnly
|
||||||
|
}
|
||||||
|
|
||||||
seUnlocker, err := secret.CreateSecureEnclaveUnlocker(
|
seUnlocker, err := secret.CreateSecureEnclaveUnlocker(
|
||||||
cli.fs, cli.stateDir, cli.Mnemonic, cli.UnlockPassphrase)
|
cli.fs, cli.stateDir, cli.Mnemonic, cli.UnlockPassphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
errMnemonicEmpty = errors.New("mnemonic cannot be empty")
|
errMnemonicEmpty = errors.New("mnemonic cannot be empty")
|
||||||
errInvalidMnemonicPhrase = errors.New("invalid BIP39 mnemonic phrase")
|
errInvalidMnemonicPhrase = errors.New("invalid BIP39 mnemonic phrase")
|
||||||
|
errInvalidMnemonic = errors.New("invalid BIP39 mnemonic")
|
||||||
errVaultHasLongTermKey = errors.New(
|
errVaultHasLongTermKey = errors.New(
|
||||||
"already has a long-term key configured")
|
"already has a long-term key configured")
|
||||||
errMnemonicEnvNotSet = errors.New(
|
errMnemonicEnvNotSet = errors.New(
|
||||||
@@ -380,7 +381,7 @@ func (cli *Instance) vaultImportPreflight(
|
|||||||
secret.Debug("Validating BIP39 mnemonic", "word_count", len(mnemonicWords))
|
secret.Debug("Validating BIP39 mnemonic", "word_count", len(mnemonicWords))
|
||||||
|
|
||||||
if !bip39.IsMnemonicValid(mnemonic) {
|
if !bip39.IsMnemonicValid(mnemonic) {
|
||||||
return "", "", "", errInvalidMnemonicPhrase
|
return "", "", "", errInvalidMnemonic
|
||||||
}
|
}
|
||||||
|
|
||||||
return vaultDir, pubKeyPath, mnemonic, nil
|
return vaultDir, pubKeyPath, mnemonic, nil
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"filippo.io/age"
|
"filippo.io/age"
|
||||||
@@ -38,6 +39,8 @@ const (
|
|||||||
var keychainItemNameRegex = regexp.MustCompile(`^[A-Za-z0-9._-]+$`)
|
var keychainItemNameRegex = regexp.MustCompile(`^[A-Za-z0-9._-]+$`)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
errNotMacOS = errors.New(
|
||||||
|
"keychain unlockers are only supported on macOS")
|
||||||
errKeychainItemNameEmpty = errors.New("keychain item name cannot be empty")
|
errKeychainItemNameEmpty = errors.New("keychain item name cannot be empty")
|
||||||
errInvalidKeychainItemName = errors.New("invalid keychain item name format")
|
errInvalidKeychainItemName = errors.New("invalid keychain item name format")
|
||||||
errUnsupportedCurrentUnlocker = errors.New(
|
errUnsupportedCurrentUnlocker = errors.New(
|
||||||
@@ -391,6 +394,12 @@ func deriveLongTermPrivateKey(
|
|||||||
func CreateKeychainUnlocker(
|
func CreateKeychainUnlocker(
|
||||||
fs afero.Fs, stateDir string, mnemonic, passphrase *memguard.LockedBuffer,
|
fs afero.Fs, stateDir string, mnemonic, passphrase *memguard.LockedBuffer,
|
||||||
) (*KeychainUnlocker, error) {
|
) (*KeychainUnlocker, error) {
|
||||||
|
// Check if we're on macOS
|
||||||
|
err := checkMacOSAvailable()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Get current vault using the GetCurrentVault function from the same package
|
// Get current vault using the GetCurrentVault function from the same package
|
||||||
vault, err := GetCurrentVault(fs, stateDir)
|
vault, err := GetCurrentVault(fs, stateDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -546,6 +555,15 @@ func writeKeychainUnlocker(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkMacOSAvailable verifies that we're running on macOS
|
||||||
|
func checkMacOSAvailable() error {
|
||||||
|
if runtime.GOOS != "darwin" {
|
||||||
|
return fmt.Errorf("%w, current OS: %s", errNotMacOS, runtime.GOOS)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// validateKeychainItemName validates that a keychain item name is safe for
|
// validateKeychainItemName validates that a keychain item name is safe for
|
||||||
// command execution
|
// command execution
|
||||||
func validateKeychainItemName(itemName string) error {
|
func validateKeychainItemName(itemName string) error {
|
||||||
|
|||||||
@@ -216,6 +216,11 @@ func CreateSecureEnclaveUnlocker(
|
|||||||
stateDir string,
|
stateDir string,
|
||||||
mnemonic, passphrase *memguard.LockedBuffer,
|
mnemonic, passphrase *memguard.LockedBuffer,
|
||||||
) (*SecureEnclaveUnlocker, error) {
|
) (*SecureEnclaveUnlocker, error) {
|
||||||
|
err := checkMacOSAvailable()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
vault, err := GetCurrentVault(fs, stateDir)
|
vault, err := GetCurrentVault(fs, stateDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get current vault: %w", err)
|
return nil, fmt.Errorf("failed to get current vault: %w", err)
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ const (
|
|||||||
maxVersionsPerDay = 999
|
maxVersionsPerDay = 999
|
||||||
)
|
)
|
||||||
|
|
||||||
var errMaxVersionsPerDay = errors.New("exceeded maximum versions per day (999)")
|
var (
|
||||||
|
errMaxVersionsPerDay = errors.New("exceeded maximum versions per day (999)")
|
||||||
// ErrNilValueBuffer is returned when a secret's value is given as nil.
|
errNilValueBuffer = errors.New("value buffer is nil")
|
||||||
var ErrNilValueBuffer = errors.New("value buffer is nil")
|
)
|
||||||
|
|
||||||
// VersionMetadata contains information about a secret version
|
// VersionMetadata contains information about a secret version
|
||||||
type VersionMetadata struct {
|
type VersionMetadata struct {
|
||||||
@@ -138,7 +138,7 @@ func GenerateVersionName(fs afero.Fs, secretDir string) (string, error) {
|
|||||||
// process dies part-way.
|
// process dies part-way.
|
||||||
func (sv *Version) Save(value *memguard.LockedBuffer) error {
|
func (sv *Version) Save(value *memguard.LockedBuffer) error {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return ErrNilValueBuffer
|
return errNilValueBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugWith("Saving secret version",
|
DebugWith("Saving secret version",
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ var (
|
|||||||
// it unlocks. Composed as "vault <name> needs a mnemonic for an unlocker".
|
// it unlocks. Composed as "vault <name> needs a mnemonic for an unlocker".
|
||||||
ErrUnlockerWithoutMnemonic = errors.New("needs a mnemonic for an unlocker")
|
ErrUnlockerWithoutMnemonic = errors.New("needs a mnemonic for an unlocker")
|
||||||
|
|
||||||
|
// ErrNilValueBuffer indicates a nil value buffer was supplied.
|
||||||
|
ErrNilValueBuffer = errors.New("value buffer is nil")
|
||||||
|
|
||||||
// ErrInvalidSecretName indicates a secret name that breaks the naming
|
// ErrInvalidSecretName indicates a secret name that breaks the naming
|
||||||
// rule: only ASCII letters, digits, '.', '-', '_' and '/'; not empty;
|
// rule: only ASCII letters, digits, '.', '-', '_' and '/'; not empty;
|
||||||
// no leading '.' or '/', no trailing '/', no '//', no '..' path segment.
|
// no leading '.' or '/', no trailing '/', no '//', no '..' path segment.
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func TestVaultErrors(t *testing.T) {
|
|||||||
}, vault.ErrVaultNotFound},
|
}, vault.ErrVaultNotFound},
|
||||||
{"add a nil value", func(vlt *vault.Vault) error {
|
{"add a nil value", func(vlt *vault.Vault) error {
|
||||||
return vlt.AddSecret(missingName, nil, false)
|
return vlt.AddSecret(missingName, nil, false)
|
||||||
}, secret.ErrNilValueBuffer},
|
}, vault.ErrNilValueBuffer},
|
||||||
{"get a missing secret", func(vlt *vault.Vault) error {
|
{"get a missing secret", func(vlt *vault.Vault) error {
|
||||||
_, err := vlt.GetSecret(missingName)
|
_, err := vlt.GetSecret(missingName)
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func ValidateSecretName(name string) error {
|
|||||||
// AddSecret adds a secret to this vault
|
// AddSecret adds a secret to this vault
|
||||||
func (v *Vault) AddSecret(name string, value *memguard.LockedBuffer, force bool) error {
|
func (v *Vault) AddSecret(name string, value *memguard.LockedBuffer, force bool) error {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return secret.ErrNilValueBuffer
|
return ErrNilValueBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
secret.DebugWith("Adding secret to vault",
|
secret.DebugWith("Adding secret to vault",
|
||||||
|
|||||||
Reference in New Issue
Block a user