Add regression test for issue #2571 (#3250)

* feat: Add regression test for issue #2571

This commit adds a regression test for issue #2571. The issue describes a scenario where `gopass sync` incorrectly removes public keys for sub-stores under certain conditions.

The new integration test in `tests/sync_test.go` reproduces the steps outlined in the GitHub issue to ensure that the public key is not deleted after running `gopass sync`.

* [fix] Fix lint errors

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

---------

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Dominik Schulz <dominik.schulz@gauner.org>
This commit is contained in:
google-labs-jules[bot]
2025-09-22 17:55:19 +02:00
committed by GitHub
parent 8c60b17c24
commit 57db06bd94
2 changed files with 108 additions and 0 deletions

View File

@@ -37,6 +37,7 @@ func (c *Client) connect() (net.Conn, error) {
}
debug.Log("connected to agent at %s", c.socketPath)
return conn, nil
}

107
tests/sync_test.go Normal file
View File

@@ -0,0 +1,107 @@
package tests
import (
"os"
"path/filepath"
"testing"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/gopasspw/gopass/tests/can"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSync(t *testing.T) {
ts := newTester(t)
defer ts.teardown()
ts.initStore()
out, err := ts.run("sync")
require.NoError(t, err)
assert.Contains(t, out, "All done")
}
func createGPGKey(t *testing.T, ts *tester, name, email string) string {
t.Helper()
e, err := openpgp.NewEntity(name, "", email, &packet.Config{
RSABits: 4096,
})
require.NoError(t, err)
for _, id := range e.Identities {
err := id.SelfSignature.SignUserId(id.UserId.Id, e.PrimaryKey, e.PrivateKey, &packet.Config{})
require.NoError(t, err)
}
el := can.EmbeddedKeyRing()
el = append(el, e)
fn := filepath.Join(ts.gpgDir(), "pubring.gpg")
fh, err := os.Create(fn)
require.NoError(t, err)
for _, e := range el {
require.NoError(t, e.Serialize(fh))
}
require.NoError(t, fh.Close())
fn = filepath.Join(ts.gpgDir(), "secring.gpg")
fh, err = os.Create(fn)
require.NoError(t, err)
for _, e := range el {
if e.PrivateKey != nil {
require.NoError(t, e.SerializePrivate(fh, nil))
}
}
require.NoError(t, fh.Close())
return e.PrimaryKey.KeyIdShortString()
}
func TestSyncKeepSubkey(t *testing.T) {
ts := newTester(t)
defer ts.teardown()
// init store
ts.initStore()
// create a new key
keyID := createGPGKey(t, ts, "sub-store-key", "sub-store-key@example.com")
// create a secret in a subdirectory
secretPath := "project_123/secret1"
out, err := ts.run("insert -f " + secretPath)
require.NoError(t, err, "failed to insert secret: %s", out)
// create .gpg-id file
subDir := filepath.Join(ts.storeDir("root"), "project_123")
require.NoError(t, os.MkdirAll(subDir, 0o755))
gpgIDFile := filepath.Join(subDir, ".gpg-id")
err = os.WriteFile(gpgIDFile, []byte(keyID), 0o644)
require.NoError(t, err)
// re-encrypt the secret
out, err = ts.run("fsck --decrypt " + filepath.Dir(secretPath))
require.NoError(t, err, "failed to fsck: %s", out)
// sync the store
out, err = ts.run("sync")
require.NoError(t, err, "failed to sync: %s", out)
// export the public key
pubKeyFile := filepath.Join(ts.storeDir("root"), ".public-keys", keyID)
out, err = ts.runCmd([]string{"gpg", "--armor", "--export", keyID}, nil)
require.NoError(t, err, "failed to export key: %s", out)
err = os.WriteFile(pubKeyFile, []byte(out), 0o644)
require.NoError(t, err)
// run sync again
out, err = ts.run("sync")
require.NoError(t, err, "failed to sync: %s", out)
// check if the public key file still exists
assert.FileExists(t, pubKeyFile, "public key file should exist")
}