Update golangci-lint to v2.12.2 with canonical config (#29)
All checks were successful
check / check (push) Successful in 43s
All checks were successful
check / check (push) Successful in 43s
Bumps golangci-lint from v2.1.6 (digest-only pin in the `Dockerfile` lint stage) to v2.12.2, pinned by tag and digest (Debian-based image). Replaces `.golangci.yml` with the canonical strict config: all linters enabled except the standard disable list (`exhaustruct`, `depguard`, `godot`, `wsl`, `wrapcheck`, `varnamelen`), `lll` at 88, `funlen` 80/50, `cyclop` 15, `dupl` 100, and test files are now linted (the old config had `tests: false`, an enable-only list of ~20 linters, `lll` 120, and a blanket exclusion of `internal/macse`). The stricter config surfaced ~1550 findings, all fixed: - `wsl_v5` (439) / `nlreturn` (24): blank-line insertions - `lll` (309): line wrapping at 88 columns; long literals split with `+` concatenation, values unchanged - `noinlineerr` (130): `if err := ...` split into assignment plus check - `paralleltest` (116): `t.Parallel()` added to tests without shared state; reasoned `//nolint` where `t.Setenv` or shared fixtures forbid it - `err113` (97): package-level sentinel errors (new `internal/vault/errors.go`), `%w` wrapping, `errors.Is` - `perfsprint` (74) / `modernize` (39) / `intrange`: `strconv`, `errors.New`, `slices.Contains`, `any`, `SplitSeq` - `goconst` (40) / `dupword` (41) / `testifylint` (42) / `thelper` (33): constants, assertion fixes, `t.Helper()` - `noctx` (22): `exec.CommandContext` for gpg/CLI invocations - `testpackage` (18): black-box tests moved to `_test` packages where they use only exported identifiers; white-box files carry a reasoned `//nolint` - `funlen`/`cyclop`/`gocognit`/`nestif`/`dupl`: behavior-preserving helper extraction - assorted singletons: `gosec`, `gosmopolitan`, `funcorder`, `nonamedreturns`, `makezero`, `prealloc`, `godox`, `nolintlint`, `ireturn`, `nilnil`, `gochecknoinits` ## User-visible strings **None changed.** Every error message this branch composes is byte-identical to the one `main` composes. The `err113` sentinels are shaped so `fmt.Errorf` reassembles the original text around them: the sentinel carries the fixed words and the caller supplies the interpolated value in the position it has always occupied. Where the value sits mid-sentence the sentinel holds only a fragment (e.g. `vault.ErrVaultNotFound` is `"does not exist"`, composed by its caller as `vault <name> does not exist`); each such sentinel documents the message it participates in. Verified mechanically, not by inspection: every `fmt.Errorf` and `errors.New` call site in both trees is parsed, the `Error()` text of any sentinel passed to `%w` is substituted in, and the resulting sets of composed message templates are compared. All 350 templates `main` produces are still produced, character for character. The set of lost or altered messages is empty. ## `unlocker list` `findUnlockerIDByMetadata` returns `(string, error)` rather than signalling failure with an empty ID, so an unreadable `unlockers.d` is no longer indistinguishable from "no matching entry". `UnlockersList` skips such an entry with a warning naming the directory — its behavior before the scan was extracted into a helper — instead of emitting a row under a synthesized fallback ID that no `unlocker remove` or `unlocker select` can match and that suppresses the current-unlocker marker. The duplicate-check and shell-completion callers skip on the same condition, matching their pre-extraction behavior. Covered by `internal/cli/unlockers_list_test.go`. `TODO.md` records the change plus follow-ups (version-completion TODOs formerly in code comments, darwin-gated files exceeding 88 columns that Linux CI does not lint). `make check` is green and the pinned v2.12.2 image reports `0 issues.` Note the test suite needs the memlock ulimit from `script/cibuild` for the 10MB memguard test; that requirement is pre-existing. Not changed: `script/bootstrap` installs golangci-lint via the system package manager (no version pin to bump), and `script/lint` invokes whatever `golangci-lint` is on PATH. golangci-lint v2.12 deprecates `gomodguard` in favor of `gomodguard_v2` (warning only); the canonical config owns that decision. Co-authored-by: sneak <sneak@sneak.berlin> Reviewed-on: #29 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #29.
This commit is contained in:
@@ -1,208 +1,243 @@
|
||||
package vault
|
||||
package vault_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.eeqj.de/sneak/secret/internal/vault"
|
||||
"git.eeqj.de/sneak/secret/pkg/agehd"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
//nolint:paralleltest // subtests share an in-memory filesystem sequentially
|
||||
func TestVaultMetadata(t *testing.T) {
|
||||
fs := afero.NewMemMapFs()
|
||||
stateDir := "/test/state"
|
||||
|
||||
// Test mnemonic for consistent testing
|
||||
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
|
||||
t.Run("ComputeDoubleSHA256", func(t *testing.T) {
|
||||
// Test data
|
||||
data := []byte("test data")
|
||||
hash := ComputeDoubleSHA256(data)
|
||||
|
||||
// Verify it's a valid hex string of 64 characters (32 bytes * 2)
|
||||
if len(hash) != 64 {
|
||||
t.Errorf("Expected hash length of 64, got %d", len(hash))
|
||||
}
|
||||
|
||||
// Verify consistency
|
||||
hash2 := ComputeDoubleSHA256(data)
|
||||
if hash != hash2 {
|
||||
t.Errorf("Hash should be consistent for same input")
|
||||
}
|
||||
|
||||
// Verify different input produces different hash
|
||||
hash3 := ComputeDoubleSHA256([]byte("different data"))
|
||||
if hash == hash3 {
|
||||
t.Errorf("Different input should produce different hash")
|
||||
}
|
||||
testComputeDoubleSHA256(t)
|
||||
})
|
||||
|
||||
t.Run("GetNextDerivationIndex", func(t *testing.T) {
|
||||
// Test with no existing vaults
|
||||
index, err := GetNextDerivationIndex(fs, stateDir, testMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
if index != 0 {
|
||||
t.Errorf("Expected index 0 for first vault, got %d", index)
|
||||
}
|
||||
|
||||
// Create a vault with metadata and matching public key
|
||||
vaultDir := filepath.Join(stateDir, "vaults.d", "vault1")
|
||||
if err := fs.MkdirAll(vaultDir, 0o700); err != nil {
|
||||
t.Fatalf("Failed to create vault directory: %v", err)
|
||||
}
|
||||
|
||||
// Derive identity for index 0
|
||||
identity0, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity: %v", err)
|
||||
}
|
||||
pubKey0 := identity0.Recipient().String()
|
||||
pubKeyHash0 := ComputeDoubleSHA256([]byte(pubKey0))
|
||||
|
||||
// Write public key
|
||||
if err := afero.WriteFile(fs, filepath.Join(vaultDir, "pub.age"), []byte(pubKey0), 0o600); err != nil {
|
||||
t.Fatalf("Failed to write public key: %v", err)
|
||||
}
|
||||
|
||||
metadata1 := &Metadata{
|
||||
DerivationIndex: 0,
|
||||
PublicKeyHash: pubKeyHash0, // Hash of the actual key (index 0)
|
||||
MnemonicFamilyHash: pubKeyHash0, // Hash of index 0 key (for family identification)
|
||||
}
|
||||
if err := SaveVaultMetadata(fs, vaultDir, metadata1); err != nil {
|
||||
t.Fatalf("Failed to save metadata: %v", err)
|
||||
}
|
||||
|
||||
// Next index for same mnemonic should be 1
|
||||
index, err = GetNextDerivationIndex(fs, stateDir, testMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
if index != 1 {
|
||||
t.Errorf("Expected index 1 for second vault with same mnemonic, got %d", index)
|
||||
}
|
||||
|
||||
// Different mnemonic should start at 0
|
||||
differentMnemonic := "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"
|
||||
index, err = GetNextDerivationIndex(fs, stateDir, differentMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
if index != 0 {
|
||||
t.Errorf("Expected index 0 for first vault with different mnemonic, got %d", index)
|
||||
}
|
||||
|
||||
// Add another vault with same mnemonic but higher index
|
||||
vaultDir2 := filepath.Join(stateDir, "vaults.d", "vault2")
|
||||
if err := fs.MkdirAll(vaultDir2, 0o700); err != nil {
|
||||
t.Fatalf("Failed to create vault directory: %v", err)
|
||||
}
|
||||
|
||||
// Derive identity for index 5
|
||||
identity5, err := agehd.DeriveIdentity(testMnemonic, 5)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity: %v", err)
|
||||
}
|
||||
pubKey5 := identity5.Recipient().String()
|
||||
|
||||
// Write public key
|
||||
if err := afero.WriteFile(fs, filepath.Join(vaultDir2, "pub.age"), []byte(pubKey5), 0o600); err != nil {
|
||||
t.Fatalf("Failed to write public key: %v", err)
|
||||
}
|
||||
|
||||
// Compute the hash for index 5 key
|
||||
pubKeyHash5 := ComputeDoubleSHA256([]byte(pubKey5))
|
||||
|
||||
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
|
||||
}
|
||||
if err := SaveVaultMetadata(fs, vaultDir2, metadata2); err != nil {
|
||||
t.Fatalf("Failed to save metadata: %v", err)
|
||||
}
|
||||
|
||||
// Next index should be 1 (not 6) because we look for the first available slot
|
||||
index, err = GetNextDerivationIndex(fs, stateDir, testMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
if index != 1 {
|
||||
t.Errorf("Expected index 1 (first available), got %d", index)
|
||||
}
|
||||
testGetNextDerivationIndex(t, fs)
|
||||
})
|
||||
|
||||
t.Run("MetadataPersistence", func(t *testing.T) {
|
||||
vaultDir := filepath.Join(stateDir, "vaults.d", "test-vault")
|
||||
if err := fs.MkdirAll(vaultDir, 0o700); err != nil {
|
||||
t.Fatalf("Failed to create vault directory: %v", err)
|
||||
}
|
||||
|
||||
// Create and save metadata
|
||||
metadata := &Metadata{
|
||||
DerivationIndex: 3,
|
||||
PublicKeyHash: "test-public-key-hash",
|
||||
}
|
||||
|
||||
if err := SaveVaultMetadata(fs, vaultDir, metadata); err != nil {
|
||||
t.Fatalf("Failed to save metadata: %v", err)
|
||||
}
|
||||
|
||||
// Load and verify
|
||||
loaded, err := LoadVaultMetadata(fs, vaultDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load metadata: %v", err)
|
||||
}
|
||||
|
||||
if loaded.DerivationIndex != metadata.DerivationIndex {
|
||||
t.Errorf("DerivationIndex mismatch: expected %d, got %d", metadata.DerivationIndex, loaded.DerivationIndex)
|
||||
}
|
||||
if loaded.PublicKeyHash != metadata.PublicKeyHash {
|
||||
t.Errorf("PublicKeyHash mismatch: expected %s, got %s", metadata.PublicKeyHash, loaded.PublicKeyHash)
|
||||
}
|
||||
testMetadataPersistence(t, fs)
|
||||
})
|
||||
|
||||
t.Run("DifferentKeysForDifferentIndices", func(t *testing.T) {
|
||||
// Derive keys with different indices
|
||||
identity0, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity with index 0: %v", err)
|
||||
}
|
||||
|
||||
identity1, err := agehd.DeriveIdentity(testMnemonic, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity with index 1: %v", err)
|
||||
}
|
||||
|
||||
// Compute public key hashes
|
||||
pubKey0 := identity0.Recipient().String()
|
||||
pubKey1 := identity1.Recipient().String()
|
||||
hash0 := ComputeDoubleSHA256([]byte(pubKey0))
|
||||
|
||||
// Verify different indices produce different public keys
|
||||
if pubKey0 == pubKey1 {
|
||||
t.Errorf("Different derivation indices should produce different public keys")
|
||||
}
|
||||
|
||||
// But the hash of index 0's public key should be the same for the same mnemonic
|
||||
// This is what we use as the identifier
|
||||
identity0Again, _ := agehd.DeriveIdentity(testMnemonic, 0)
|
||||
pubKey0Again := identity0Again.Recipient().String()
|
||||
hash0Again := ComputeDoubleSHA256([]byte(pubKey0Again))
|
||||
|
||||
if hash0 != hash0Again {
|
||||
t.Errorf("Same mnemonic should produce same public key hash for index 0")
|
||||
}
|
||||
testDifferentKeysForDifferentIndices(t)
|
||||
})
|
||||
}
|
||||
|
||||
func testComputeDoubleSHA256(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
// Test data
|
||||
data := []byte("test data")
|
||||
hash := vault.ComputeDoubleSHA256(data)
|
||||
|
||||
// Verify it's a valid hex string of 64 characters (32 bytes * 2)
|
||||
if len(hash) != 64 {
|
||||
t.Errorf("Expected hash length of 64, got %d", len(hash))
|
||||
}
|
||||
|
||||
// Verify consistency
|
||||
hash2 := vault.ComputeDoubleSHA256(data)
|
||||
if hash != hash2 {
|
||||
t.Errorf("Hash should be consistent for same input")
|
||||
}
|
||||
|
||||
// Verify different input produces different hash
|
||||
hash3 := vault.ComputeDoubleSHA256([]byte("different data"))
|
||||
if hash == hash3 {
|
||||
t.Errorf("Different input should produce different hash")
|
||||
}
|
||||
}
|
||||
|
||||
// createVaultDirWithMetadata creates a vault directory containing a public
|
||||
// key derived from testMnemonic at the given index plus saved metadata, and
|
||||
// returns the derived public key hash. An empty familyHash defaults to the
|
||||
// derived key's own hash.
|
||||
func createVaultDirWithMetadata(
|
||||
t *testing.T, fs afero.Fs, vaultName string,
|
||||
derivationIndex uint32, familyHash string,
|
||||
) string {
|
||||
t.Helper()
|
||||
|
||||
vaultDir := filepath.Join(testStateDir, "vaults.d", vaultName)
|
||||
|
||||
err := fs.MkdirAll(vaultDir, 0o700)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create vault directory: %v", err)
|
||||
}
|
||||
|
||||
// Derive identity for the requested index
|
||||
identity, err := agehd.DeriveIdentity(testMnemonic, derivationIndex)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity: %v", err)
|
||||
}
|
||||
|
||||
pubKey := identity.Recipient().String()
|
||||
pubKeyHash := vault.ComputeDoubleSHA256([]byte(pubKey))
|
||||
|
||||
// Write public key
|
||||
err = afero.WriteFile(fs, filepath.Join(vaultDir, "pub.age"),
|
||||
[]byte(pubKey), 0o600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write public key: %v", err)
|
||||
}
|
||||
|
||||
if familyHash == "" {
|
||||
familyHash = pubKeyHash
|
||||
}
|
||||
|
||||
metadata := &vault.Metadata{
|
||||
DerivationIndex: derivationIndex,
|
||||
PublicKeyHash: pubKeyHash,
|
||||
MnemonicFamilyHash: familyHash,
|
||||
}
|
||||
|
||||
err = vault.SaveVaultMetadata(fs, vaultDir, metadata)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save metadata: %v", err)
|
||||
}
|
||||
|
||||
return pubKeyHash
|
||||
}
|
||||
|
||||
func testGetNextDerivationIndex(t *testing.T, fs afero.Fs) {
|
||||
t.Helper()
|
||||
|
||||
// Test with no existing vaults
|
||||
index, err := vault.GetNextDerivationIndex(fs, testStateDir, testMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
|
||||
if index != 0 {
|
||||
t.Errorf("Expected index 0 for first vault, got %d", index)
|
||||
}
|
||||
|
||||
// Create a vault with metadata and matching public key (index 0; the
|
||||
// family hash is the index 0 key hash)
|
||||
pubKeyHash0 := createVaultDirWithMetadata(t, fs, "vault1", 0, "")
|
||||
|
||||
// Next index for same mnemonic should be 1
|
||||
index, err = vault.GetNextDerivationIndex(fs, testStateDir, testMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
|
||||
if index != 1 {
|
||||
t.Errorf("Expected index 1 for second vault with same mnemonic, got %d", index)
|
||||
}
|
||||
|
||||
// Different mnemonic should start at 0
|
||||
//nolint:dupword // BIP39-style test mnemonic
|
||||
differentMnemonic := "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"
|
||||
|
||||
index, err = vault.GetNextDerivationIndex(fs, testStateDir, differentMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
|
||||
if index != 0 {
|
||||
t.Errorf("Expected index 0 for first vault with different mnemonic, got %d",
|
||||
index)
|
||||
}
|
||||
|
||||
// Add another vault with same mnemonic but higher index (5), sharing
|
||||
// the same family hash since it's from the same mnemonic
|
||||
createVaultDirWithMetadata(t, fs, "vault2", 5, pubKeyHash0)
|
||||
|
||||
// Next index should be 1 (not 6): we look for the first available slot
|
||||
index, err = vault.GetNextDerivationIndex(fs, testStateDir, testMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get derivation index: %v", err)
|
||||
}
|
||||
|
||||
if index != 1 {
|
||||
t.Errorf("Expected index 1 (first available), got %d", index)
|
||||
}
|
||||
}
|
||||
|
||||
func testMetadataPersistence(t *testing.T, fs afero.Fs) {
|
||||
t.Helper()
|
||||
|
||||
vaultDir := filepath.Join(testStateDir, "vaults.d", testVaultName)
|
||||
|
||||
err := fs.MkdirAll(vaultDir, 0o700)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create vault directory: %v", err)
|
||||
}
|
||||
|
||||
// Create and save metadata
|
||||
metadata := &vault.Metadata{
|
||||
DerivationIndex: 3,
|
||||
PublicKeyHash: "test-public-key-hash",
|
||||
}
|
||||
|
||||
err = vault.SaveVaultMetadata(fs, vaultDir, metadata)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save metadata: %v", err)
|
||||
}
|
||||
|
||||
// Load and verify
|
||||
loaded, err := vault.LoadVaultMetadata(fs, vaultDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load metadata: %v", err)
|
||||
}
|
||||
|
||||
if loaded.DerivationIndex != metadata.DerivationIndex {
|
||||
t.Errorf("DerivationIndex mismatch: expected %d, got %d",
|
||||
metadata.DerivationIndex, loaded.DerivationIndex)
|
||||
}
|
||||
|
||||
if loaded.PublicKeyHash != metadata.PublicKeyHash {
|
||||
t.Errorf("PublicKeyHash mismatch: expected %s, got %s",
|
||||
metadata.PublicKeyHash, loaded.PublicKeyHash)
|
||||
}
|
||||
}
|
||||
|
||||
func testDifferentKeysForDifferentIndices(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
// Derive keys with different indices
|
||||
identity0, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity with index 0: %v", err)
|
||||
}
|
||||
|
||||
identity1, err := agehd.DeriveIdentity(testMnemonic, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity with index 1: %v", err)
|
||||
}
|
||||
|
||||
// Compute public key hashes
|
||||
pubKey0 := identity0.Recipient().String()
|
||||
pubKey1 := identity1.Recipient().String()
|
||||
hash0 := vault.ComputeDoubleSHA256([]byte(pubKey0))
|
||||
|
||||
// Verify different indices produce different public keys
|
||||
if pubKey0 == pubKey1 {
|
||||
t.Errorf("Different derivation indices should produce different public keys")
|
||||
}
|
||||
|
||||
// But the hash of index 0's public key should be the same for the same
|
||||
// mnemonic. This is what we use as the identifier
|
||||
identity0Again, _ := agehd.DeriveIdentity(testMnemonic, 0)
|
||||
pubKey0Again := identity0Again.Recipient().String()
|
||||
hash0Again := vault.ComputeDoubleSHA256([]byte(pubKey0Again))
|
||||
|
||||
if hash0 != hash0Again {
|
||||
t.Errorf("Same mnemonic should produce same public key hash for index 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKeyHashConsistency(t *testing.T) {
|
||||
// Use the same test mnemonic that the integration test uses
|
||||
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
t.Parallel()
|
||||
|
||||
// Derive identity from index 0 multiple times
|
||||
identity1, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||
@@ -223,8 +258,8 @@ func TestPublicKeyHashConsistency(t *testing.T) {
|
||||
}
|
||||
|
||||
// Compute public key hashes
|
||||
hash1 := ComputeDoubleSHA256([]byte(identity1.Recipient().String()))
|
||||
hash2 := ComputeDoubleSHA256([]byte(identity2.Recipient().String()))
|
||||
hash1 := vault.ComputeDoubleSHA256([]byte(identity1.Recipient().String()))
|
||||
hash2 := vault.ComputeDoubleSHA256([]byte(identity2.Recipient().String()))
|
||||
|
||||
// Verify hashes are the same
|
||||
if hash1 != hash2 {
|
||||
@@ -237,11 +272,15 @@ func TestPublicKeyHashConsistency(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSampleHashCalculation(t *testing.T) {
|
||||
// Test with the exact mnemonic from integration test if available
|
||||
// We'll also test with a few different mnemonics to make sure they produce different hashes
|
||||
t.Parallel()
|
||||
|
||||
// Test with the exact mnemonic from integration test if available. We
|
||||
// also test with a few different mnemonics to make sure they produce
|
||||
// different hashes
|
||||
mnemonics := []string{
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
testMnemonic,
|
||||
"legal winner thank year wave sausage worth useful legal winner thank yellow",
|
||||
//nolint:dupword // BIP39-style test mnemonic
|
||||
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
|
||||
}
|
||||
|
||||
@@ -251,29 +290,29 @@ func TestSampleHashCalculation(t *testing.T) {
|
||||
t.Fatalf("Failed to derive identity for mnemonic %d: %v", i, err)
|
||||
}
|
||||
|
||||
hash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||
hash := vault.ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||
t.Logf("Mnemonic %d hash (index 0): %s", i, hash)
|
||||
t.Logf(" Recipient: %s", identity.Recipient().String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkflowMismatch(t *testing.T) {
|
||||
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
|
||||
// Create a temporary directory for testing
|
||||
tempDir := t.TempDir()
|
||||
fs := afero.NewOsFs()
|
||||
|
||||
// Test Case 1: Create vault WITH mnemonic (like init command)
|
||||
t.Setenv("SB_SECRET_MNEMONIC", testMnemonic)
|
||||
_, err := CreateVault(fs, tempDir, "default")
|
||||
|
||||
_, err := vault.CreateVault(fs, tempDir, "default")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create vault with mnemonic: %v", err)
|
||||
}
|
||||
|
||||
// Load metadata for vault1
|
||||
vault1Dir := filepath.Join(tempDir, "vaults.d", "default")
|
||||
metadata1, err := LoadVaultMetadata(fs, vault1Dir)
|
||||
|
||||
metadata1, err := vault.LoadVaultMetadata(fs, vault1Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load vault1 metadata: %v", err)
|
||||
}
|
||||
@@ -281,9 +320,10 @@ func TestWorkflowMismatch(t *testing.T) {
|
||||
t.Logf("Vault1 (with mnemonic) - DerivationIndex: %d, PublicKeyHash: %s",
|
||||
metadata1.DerivationIndex, metadata1.PublicKeyHash)
|
||||
|
||||
// Test Case 2: Create vault WITHOUT mnemonic, then import (like work vault)
|
||||
// Test Case 2: Create vault WITHOUT mnemonic, then import (work vault)
|
||||
t.Setenv("SB_SECRET_MNEMONIC", "")
|
||||
_, err = CreateVault(fs, tempDir, "work")
|
||||
|
||||
_, err = vault.CreateVault(fs, tempDir, "work")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create vault without mnemonic: %v", err)
|
||||
}
|
||||
@@ -294,7 +334,7 @@ func TestWorkflowMismatch(t *testing.T) {
|
||||
t.Setenv("SB_SECRET_MNEMONIC", testMnemonic)
|
||||
|
||||
// Get the next available derivation index for this mnemonic
|
||||
derivationIndex, err := GetNextDerivationIndex(fs, tempDir, testMnemonic)
|
||||
derivationIndex, err := vault.GetNextDerivationIndex(fs, tempDir, testMnemonic)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get next derivation index: %v", err)
|
||||
}
|
||||
@@ -306,10 +346,12 @@ func TestWorkflowMismatch(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity for index 0: %v", err)
|
||||
}
|
||||
publicKeyHash := ComputeDoubleSHA256([]byte(identity0.Recipient().String()))
|
||||
|
||||
publicKeyHash := vault.ComputeDoubleSHA256(
|
||||
[]byte(identity0.Recipient().String()))
|
||||
|
||||
// Load existing metadata and update it (same as in VaultImport)
|
||||
existingMetadata, err := LoadVaultMetadata(fs, vault2Dir)
|
||||
existingMetadata, err := vault.LoadVaultMetadata(fs, vault2Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load existing metadata: %v", err)
|
||||
}
|
||||
@@ -318,12 +360,13 @@ func TestWorkflowMismatch(t *testing.T) {
|
||||
existingMetadata.DerivationIndex = derivationIndex
|
||||
existingMetadata.PublicKeyHash = publicKeyHash
|
||||
|
||||
if err := SaveVaultMetadata(fs, vault2Dir, existingMetadata); err != nil {
|
||||
err = vault.SaveVaultMetadata(fs, vault2Dir, existingMetadata)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to save vault metadata: %v", err)
|
||||
}
|
||||
|
||||
// Load updated metadata for vault2
|
||||
metadata2, err := LoadVaultMetadata(fs, vault2Dir)
|
||||
metadata2, err := vault.LoadVaultMetadata(fs, vault2Dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load vault2 metadata: %v", err)
|
||||
}
|
||||
@@ -337,57 +380,59 @@ func TestWorkflowMismatch(t *testing.T) {
|
||||
t.Logf("Vault1 hash: %s", metadata1.PublicKeyHash)
|
||||
t.Logf("Vault2 hash: %s", metadata2.PublicKeyHash)
|
||||
} else {
|
||||
t.Logf("SUCCESS: Both vaults have the same public key hash: %s", metadata1.PublicKeyHash)
|
||||
t.Logf("SUCCESS: Both vaults have the same public key hash: %s",
|
||||
metadata1.PublicKeyHash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseEngineerHash(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// This is the hash that the work vault is getting in the failing test
|
||||
wrongHash := "e34a2f500e395d8934a90a99ee9311edcfffd68cb701079575e50cbac7bb9417"
|
||||
correctHash := "992552b00b3879dfae461fab9a084b47784a032771c7a9accaebdde05ec7a7d1"
|
||||
|
||||
// Test mnemonic from integration test
|
||||
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
|
||||
// Calculate hash for test mnemonic
|
||||
identity, err := agehd.DeriveIdentity(testMnemonic, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to derive identity: %v", err)
|
||||
}
|
||||
|
||||
calculatedHash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||
calculatedHash := vault.ComputeDoubleSHA256(
|
||||
[]byte(identity.Recipient().String()))
|
||||
t.Logf("Test mnemonic hash: %s", calculatedHash)
|
||||
|
||||
if calculatedHash == correctHash {
|
||||
t.Logf("✓ Test mnemonic produces the correct hash")
|
||||
t.Logf("Test mnemonic produces the correct hash")
|
||||
} else {
|
||||
t.Errorf("✗ Test mnemonic does not produce the correct hash")
|
||||
t.Errorf("Test mnemonic does not produce the correct hash")
|
||||
}
|
||||
|
||||
if calculatedHash == wrongHash {
|
||||
t.Logf("✗ Test mnemonic unexpectedly produces the wrong hash")
|
||||
t.Logf("Test mnemonic unexpectedly produces the wrong hash")
|
||||
}
|
||||
|
||||
// Let's try some other possibilities - maybe there's a string normalization issue?
|
||||
// Try some other possibilities: maybe a string normalization issue?
|
||||
variations := []string{
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
" abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about ",
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\n",
|
||||
strings.TrimSpace("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"),
|
||||
testMnemonic,
|
||||
" " + testMnemonic + " ",
|
||||
testMnemonic + "\n",
|
||||
strings.TrimSpace(testMnemonic),
|
||||
}
|
||||
|
||||
for i, variation := range variations {
|
||||
identity, err := agehd.DeriveIdentity(variation, 0)
|
||||
if err != nil {
|
||||
t.Logf("Variation %d failed: %v", i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
hash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||
hash := vault.ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||
t.Logf("Variation %d hash: %s", i, hash)
|
||||
|
||||
if hash == wrongHash {
|
||||
t.Logf("✗ Found variation that produces wrong hash: '%s'", variation)
|
||||
t.Logf("Found variation that produces wrong hash: '%s'", variation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,14 +446,15 @@ func TestReverseEngineerHash(t *testing.T) {
|
||||
identity, err := agehd.DeriveIdentity(emptyMnemonic, 0)
|
||||
if err != nil {
|
||||
t.Logf("Empty mnemonic %d failed (expected): %v", i, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
hash := ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||
hash := vault.ComputeDoubleSHA256([]byte(identity.Recipient().String()))
|
||||
t.Logf("Empty mnemonic %d hash: %s", i, hash)
|
||||
|
||||
if hash == wrongHash {
|
||||
t.Logf("✗ Empty mnemonic produces wrong hash!")
|
||||
t.Logf("Empty mnemonic produces wrong hash!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user