Files
incredigo/internal/sink/bundle_test.go
T
leetcrypt 09cd7208d6 sink: strip leading dots per path segment in slug() (backup-gate coverage fix)
An env credential's Identity (".env / API_TOKEN") produced StorePath
imported/env/.env/api_token; gopass hides dot-prefixed segments from
`ls --flat`, so those entries were invisible to bundle.ListPaths and the
mandatory backup gate silently under-covered them (Hard Rule #1 violation).
slug() now strips leading dots per /-separated segment so incredigo can no
longer write a path that `ls --flat` hides. Locked in by
TestStorePathNoDotSegment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-18 14:48:36 -07:00

138 lines
3.9 KiB
Go

package sink
import (
"bytes"
"context"
"os"
"path/filepath"
"strings"
"testing"
"incredigo/internal/discover"
)
// writeFakeGopass writes a stub `gopass` binary that stores entries as files under
// $FAKE_GOPASS_STORE, supporting the ls/show/insert subcommands the bundle code
// uses. This exercises the real framing + subprocess plumbing without touching the
// user's actual gopass store.
func writeFakeGopass(t *testing.T) string {
t.Helper()
bin := filepath.Join(t.TempDir(), "gopass")
script := `#!/usr/bin/env bash
store="$FAKE_GOPASS_STORE"
case "$1" in
ls) (cd "$store" 2>/dev/null && find . -type f | sed 's#^\./##' | sort) ;;
show) cat "$store/$3" ;;
insert)
shift; path=""
for a in "$@"; do case "$a" in --multiline=false|-f) ;; *) path="$a";; esac; done
mkdir -p "$store/$(dirname "$path")"; cat > "$store/$path" ;;
esac
`
if err := os.WriteFile(bin, []byte(script), 0o755); err != nil {
t.Fatal(err)
}
return bin
}
func seed(t *testing.T, store string, entries map[string]string) {
t.Helper()
for path, secret := range entries {
full := filepath.Join(store, path)
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(full, []byte(secret), 0o600); err != nil {
t.Fatal(err)
}
}
}
func TestBundleRoundTrip(t *testing.T) {
bin := writeFakeGopass(t)
gp := &Gopass{Bin: bin}
ctx := context.Background()
src := t.TempDir()
seed(t, src, map[string]string{
"imported/aws/default": "AKIA/secret+with=specials",
"imported/env/api": "unicode-末-\U0001f511",
"other/skip": "should-not-export",
})
// Export the imported/ subtree to a framed (unsealed) buffer.
t.Setenv("FAKE_GOPASS_STORE", src)
var buf bytes.Buffer
n, err := gp.ExportTo(ctx, &buf, "imported/")
if err != nil {
t.Fatal(err)
}
if n != 2 {
t.Fatalf("exported %d entries, want 2 (other/ excluded by prefix)", n)
}
// Import into a fresh store and assert byte-exact restoration.
dst := t.TempDir()
t.Setenv("FAKE_GOPASS_STORE", dst)
m, err := gp.ImportFrom(ctx, &buf, true)
if err != nil {
t.Fatal(err)
}
if m != 2 {
t.Fatalf("imported %d entries, want 2", m)
}
want := map[string]string{
"imported/aws/default": "AKIA/secret+with=specials",
"imported/env/api": "unicode-末-\U0001f511",
}
for path, exp := range want {
got, err := os.ReadFile(filepath.Join(dst, path))
if err != nil {
t.Fatalf("restored %s: %v", path, err)
}
if string(got) != exp {
t.Errorf("%s: got %q want %q", path, got, exp)
}
}
if _, err := os.Stat(filepath.Join(dst, "other", "skip")); !os.IsNotExist(err) {
t.Error("other/skip leaked across the prefix filter")
}
}
func TestStorePathNoDoubleSegment(t *testing.T) {
gp := &Gopass{Prefix: "imported/"}
c := discover.Credential{Source: "file", Identity: "keys/deploy"}
if got := gp.StorePath(c); got != "imported/file/keys/deploy" {
t.Errorf("StorePath = %q, want imported/file/keys/deploy", got)
}
}
// TestStorePathNoDotSegment guards Hard Rule #1: a dot-prefixed path segment
// (e.g. ".env") is hidden by gopass `ls --flat`, which would make the entry
// invisible to the backup gate's coverage check. slug() must strip leading
// dots so every stored credential is enumerable and therefore backed up.
func TestStorePathNoDotSegment(t *testing.T) {
gp := &Gopass{Prefix: "imported/"}
cases := []struct {
identity string
want string
}{
{".env / API_TOKEN", "imported/env/env/api_token"},
{".env.prod / DATABASE_URL", "imported/env/env.prod/database_url"},
{".netrc / password", "imported/env/netrc/password"},
}
for _, tc := range cases {
c := discover.Credential{Source: "env", Identity: tc.identity}
got := gp.StorePath(c)
if got != tc.want {
t.Errorf("StorePath(%q) = %q, want %q", tc.identity, got, tc.want)
}
for _, seg := range strings.Split(got, "/") {
if strings.HasPrefix(seg, ".") {
t.Errorf("StorePath(%q) = %q has hidden dot-segment %q", tc.identity, got, seg)
}
}
}
}