feat: share.keyRules, how long and varied a share key must be
Sharing refuses a key shorter than minLength characters (default 32, never under 16) or of fewer than minClasses kinds of lowercase, uppercase, digits and symbols (default 2, so random hex passes), saying so without showing it. Opening doesn't check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
13 files changed
+156
-21
No files matched your search
@@ -23,7 +23,7 @@ func TestSealOpen(t *testing.T) {
|
||||
{Path: ".env", Data: []byte("DB_PASSWORD=hunter2\n")},
|
||||
{Path: "apps/api/.env.local", Data: []byte("TOKEN=\"a b\" # quoted\n")},
|
||||
}
|
||||
link, err := Seal(Header{Key: key, Repo: "git.example.com/team/app"}, pass, files)
|
||||
link, err := Seal(Header{Key: key, Repo: "git.example.com/team/app"}, pass, files, Rules{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestSealRefuses(t *testing.T) {
|
||||
files []File
|
||||
want string
|
||||
}{
|
||||
"short key": {Header{Key: key}, []byte("short"), ok, "at least 16"},
|
||||
"short key": {Header{Key: key}, []byte("short"), ok, "keyRules wants"},
|
||||
"vault as key": {Header{Key: ref.Ref{Kind: provider.KindAzure, Host: "kv.vault.azure.net"}}, pass, ok, "isn't a secret"},
|
||||
"no files": {Header{Key: key}, pass, nil, "no files"},
|
||||
"not env": {Header{Key: key}, pass, []File{{Path: "main.go", Data: []byte("x")}}, "isn't an env file"},
|
||||
@@ -70,7 +70,7 @@ func TestSealRefuses(t *testing.T) {
|
||||
"binary": {Header{Key: key}, pass, []File{{Path: ".env", Data: []byte{0xff, 0xfe}}}, "isn't text"},
|
||||
"too big": {Header{Key: key}, pass, []File{{Path: ".env", Data: []byte(random(2 * MaxLink))}}, "more than the 16 KB"},
|
||||
} {
|
||||
if _, err := Seal(c.h, c.pass, c.files); err == nil || !strings.Contains(err.Error(), c.want) {
|
||||
if _, err := Seal(c.h, c.pass, c.files, Rules{}); err == nil || !strings.Contains(err.Error(), c.want) {
|
||||
t.Errorf("%s: %v, want %q", name, err, c.want)
|
||||
}
|
||||
}
|
||||
@@ -89,8 +89,31 @@ func random(n int) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func TestRules(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
r Rules
|
||||
key string
|
||||
pass bool
|
||||
}{
|
||||
{Rules{}, "q2Hd8bT4Lw1sZp9Kf6Vx3Nc7Rm0Ya5Ue", true}, // base64-like, 32
|
||||
{Rules{}, "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", true}, // hex
|
||||
{Rules{}, "correct-horse-battery-staple", false}, // 28: short
|
||||
{Rules{}, strings.Repeat("a", 40), false}, // one kind
|
||||
{Rules{MinLength: 16, MinClasses: 1}, strings.Repeat("a", 16), true},
|
||||
{Rules{MinLength: 4}, "abc123", false}, // never under MinKey
|
||||
{Rules{MinClasses: 4}, "q2Hd8bT4Lw1sZp9Kf6Vx3Nc7Rm0Ya5Ue", false},
|
||||
{Rules{MinClasses: 4}, "q2Hd8bT4Lw1sZp9Kf6Vx3Nc7Rm0Ya5U+", true},
|
||||
} {
|
||||
if err := c.r.Check([]byte(c.key)); (err == nil) != c.pass {
|
||||
t.Errorf("%+v %q: %v", c.r, c.key, err)
|
||||
} else if err != nil && strings.Contains(err.Error(), c.key) {
|
||||
t.Errorf("the error shows the key: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadGarbled(t *testing.T) {
|
||||
link, err := Seal(Header{Key: key}, pass, []File{{Path: ".env", Data: []byte("A=1")}})
|
||||
link, err := Seal(Header{Key: key}, pass, []File{{Path: ".env", Data: []byte("A=1")}}, Rules{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user