feat: H shows what's hidden; Spring keys that don't look secret are hidden (#81)
Co-authored-by: Karl-Einar Bergstrøm <karl-einarb@outlook.com>
This commit was merged in pull request #81.
This commit is contained in:
1 parent
17da9eaa32
commit
913b3db6a9
19 files changed
+323
-74
No files matched your search
@@ -190,12 +190,19 @@ func (p *Provider) Current(ctx context.Context) (provider.Source, bool) {
|
||||
return provider.Source{}, false
|
||||
}
|
||||
src := p.source(r)
|
||||
if files, err := p.Containers(ctx, src); err == nil && len(files) == 0 {
|
||||
files, err := p.Containers(ctx, src)
|
||||
if err == nil && len(files) == 0 {
|
||||
return provider.Source{}, false
|
||||
}
|
||||
src.Hidden = err == nil && allHidden(files)
|
||||
return src, true
|
||||
}
|
||||
|
||||
// allHidden reports whether a repo's files are all hidden: the repo is too.
|
||||
func allHidden(files []provider.Container) bool {
|
||||
return !slices.ContainsFunc(files, func(c provider.Container) bool { return !c.Hidden })
|
||||
}
|
||||
|
||||
func (p *Provider) source(r repoInfo) provider.Source {
|
||||
return provider.Source{
|
||||
Kind: provider.KindRepo, Provider: p.ID(), ProviderLabel: p.Label(),
|
||||
@@ -264,6 +271,7 @@ func (p *Provider) Sources(ctx context.Context) ([]provider.Source, error) {
|
||||
parallel(len(srcs), func(i int) {
|
||||
files, err := p.Containers(ctx, srcs[i])
|
||||
keep[i] = err != nil || len(files) > 0
|
||||
srcs[i].Hidden = err == nil && allHidden(files)
|
||||
})
|
||||
var out []provider.Source
|
||||
for i, src := range srcs {
|
||||
@@ -327,6 +335,7 @@ func (p *Provider) Containers(ctx context.Context, src provider.Source) ([]provi
|
||||
c.Detail = provider.DetailPlainEnv
|
||||
case IsSpring(f):
|
||||
c.Detail = "spring · " + springFormat(f)
|
||||
c.Hidden = p.springHidden(r.root, path)
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -331,7 +332,22 @@ func unescapeProperty(s string) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// springObjects lists the file's secret keys.
|
||||
// springHidden reports whether a Spring file has no secret keys. One that
|
||||
// can't be read isn't hidden: its error is worth seeing.
|
||||
func (p *Provider) springHidden(root, file string) bool {
|
||||
keys, err := readSpring(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
filter, err := p.springFilterFor(root)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !slices.ContainsFunc(keys, filter.secret)
|
||||
}
|
||||
|
||||
// springObjects lists the file's keys, those that don't look like secrets
|
||||
// hidden.
|
||||
func (p *Provider) springObjects(c provider.Container, b []byte) ([]provider.Object, error) {
|
||||
keys, err := parseSpring(c.ID, b)
|
||||
if err != nil {
|
||||
@@ -344,9 +360,6 @@ func (p *Provider) springObjects(c provider.Container, b []byte) ([]provider.Obj
|
||||
format := provider.Attr{Label: "format", Value: "spring " + springFormat(c.ID)}
|
||||
var out []provider.Object
|
||||
for _, k := range keys {
|
||||
if !filter.secret(k) {
|
||||
continue
|
||||
}
|
||||
state, detail := provider.Attr{Label: "encrypted", Value: "no: plain text in the file"}, ""
|
||||
switch {
|
||||
case k.reference():
|
||||
@@ -356,7 +369,7 @@ func (p *Provider) springObjects(c provider.Container, b []byte) ([]provider.Obj
|
||||
}
|
||||
out = append(out, provider.Object{
|
||||
Container: c, Type: provider.TypeSecret, ID: c.ID + "\x00" + k.name, Name: k.name, Detail: detail,
|
||||
Attrs: []provider.Attr{format, state},
|
||||
Hidden: !filter.secret(k), Attrs: []provider.Attr{format, state},
|
||||
})
|
||||
}
|
||||
return out, nil
|
||||
@@ -406,6 +419,6 @@ func (p *Provider) springDetails(c provider.Container) (attrs []provider.Attr, p
|
||||
}
|
||||
return []provider.Attr{
|
||||
{Label: "encryption", Value: jasyptShare(jasypt, len(keys))},
|
||||
{Label: "other keys", Value: fmt.Sprintf("%d, not matched by the secret patterns", len(keys)-secrets)},
|
||||
{Label: "hidden keys", Value: fmt.Sprintf("%d, not matched by the secret patterns", len(keys)-secrets)},
|
||||
}, plain
|
||||
}
|
||||
@@ -177,11 +177,15 @@ app:
|
||||
}
|
||||
local, yml := cs[0], cs[1]
|
||||
|
||||
keys, _ := p.Objects(ctx, yml, provider.TypeSecret)
|
||||
listed, _ := p.Objects(ctx, yml, provider.TypeSecret)
|
||||
keys := shown(listed)
|
||||
var names []string
|
||||
for _, k := range keys {
|
||||
names = append(names, strings.TrimSpace(k.Name+" "+k.Detail))
|
||||
}
|
||||
if len(listed) != 5 || !listed[0].Hidden || listed[0].Name != "server.port" {
|
||||
t.Errorf("hidden keys: %+v", listed)
|
||||
}
|
||||
if want := []string{"spring.datasource.password reference", "jasypt.encryptor.password reference", "app.api-key jasypt"}; !slices.Equal(names, want) {
|
||||
t.Errorf("keys %v, want %v", names, want)
|
||||
}
|
||||
@@ -198,7 +202,7 @@ app:
|
||||
}
|
||||
|
||||
// References and Jasypt aren't plain text in git; a password is.
|
||||
if d := details(p, yml); d["other keys"].Value != "2, not matched by the secret patterns" || d["git"].Warn || d["encryption"].Value != "jasypt: 1 of 5 values" {
|
||||
if d := details(p, yml); d["hidden keys"].Value != "2, not matched by the secret patterns" || d["git"].Warn || d["encryption"].Value != "jasypt: 1 of 5 values" {
|
||||
t.Errorf("yml: %+v", d)
|
||||
}
|
||||
if d := details(p, local); !d["git"].Warn {
|
||||
@@ -208,12 +212,12 @@ app:
|
||||
// The config's patterns, and the repo's over them.
|
||||
all := []string{".*"}
|
||||
p = New(Settings{Dir: root, Spring: SpringPatterns{Keys: &all}})
|
||||
if keys, _ := p.Objects(ctx, yml, provider.TypeSecret); len(keys) != 5 {
|
||||
t.Errorf("every key: %d", len(keys))
|
||||
if keys, _ := p.Objects(ctx, yml, provider.TypeSecret); len(shown(keys)) != 5 {
|
||||
t.Errorf("every key: %d", len(shown(keys)))
|
||||
}
|
||||
lazysecrets := filepath.Join(root, ".lazysecrets.yaml")
|
||||
os.WriteFile(lazysecrets, []byte("spring:\n keys: [\"url$\"]\n values: []\n"), 0o644)
|
||||
if keys, _ := p.Objects(ctx, yml, provider.TypeSecret); len(keys) != 1 || keys[0].Name != "spring.datasource.url" {
|
||||
if keys, _ := p.Objects(ctx, yml, provider.TypeSecret); len(shown(keys)) != 1 || shown(keys)[0].Name != "spring.datasource.url" {
|
||||
t.Errorf("repo's patterns: %v", keys)
|
||||
}
|
||||
os.WriteFile(lazysecrets, []byte("spring:\n keys: [\"(\"]\n"), 0o644)
|
||||
@@ -221,3 +225,31 @@ app:
|
||||
t.Errorf("bad pattern: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func shown(objects []provider.Object) []provider.Object {
|
||||
return slices.DeleteFunc(slices.Clone(objects), func(o provider.Object) bool { return o.Hidden })
|
||||
}
|
||||
|
||||
func TestHiddenFiles(t *testing.T) {
|
||||
root := gitRepo(t, map[string]string{
|
||||
"a/application.yml": "server:\n port: 8080\n",
|
||||
"b/application.yml": "spring.datasource.password: x\n",
|
||||
})
|
||||
ctx := context.Background()
|
||||
p := New(Settings{Dir: root})
|
||||
srcs, _ := p.Sources(ctx)
|
||||
if len(srcs) != 1 || srcs[0].Hidden {
|
||||
t.Fatalf("sources: %+v", srcs)
|
||||
}
|
||||
cs, _ := p.Containers(ctx, srcs[0])
|
||||
if len(cs) != 2 || !cs[0].Hidden || cs[1].Hidden {
|
||||
t.Errorf("files: %+v", cs)
|
||||
}
|
||||
|
||||
// A repo with only hidden files is hidden too.
|
||||
root = gitRepo(t, map[string]string{"application.yml": "server:\n port: 8080\n"})
|
||||
srcs, _ = New(Settings{Dir: root}).Sources(ctx)
|
||||
if len(srcs) != 1 || !srcs[0].Hidden {
|
||||
t.Errorf("hidden repo: %+v", srcs)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user