From 6cba750a997cac6bbd3bff7ffb97ca73178e980b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Gro=C3=9Fmann?= Date: Thu, 29 Jan 2026 13:59:29 +0100 Subject: [PATCH] feat: n replace --- runtime/internal/launcher/plugin_test.go | 8 +++ x/api/resolver/resolver_test.go | 8 +++ x/secrets/pattern.go | 36 +++++++++++++ x/secrets/pattern_test.go | 69 ++++++++++++++++++++++++ 4 files changed, 121 insertions(+) diff --git a/runtime/internal/launcher/plugin_test.go b/runtime/internal/launcher/plugin_test.go index 2a7e57d3..e7bc1bba 100644 --- a/runtime/internal/launcher/plugin_test.go +++ b/runtime/internal/launcher/plugin_test.go @@ -339,6 +339,14 @@ func (m maliciousPattern) String() string { return "a*a" } +func (m maliciousPattern) ExpandID(secrets.ID) (secrets.ID, error) { + panic("implement me") +} + +func (m maliciousPattern) ExpandPattern(secrets.Pattern) (secrets.Pattern, error) { + panic("implement me") +} + func testExternalPluginConfig(t *testing.T) p.Config { t.Helper() return p.Config{Version: api.MustNewVersion("v4"), Pattern: mockPattern, Logger: testhelper.TestLogger(t)} diff --git a/x/api/resolver/resolver_test.go b/x/api/resolver/resolver_test.go index f1f012b4..eba7f8dc 100644 --- a/x/api/resolver/resolver_test.go +++ b/x/api/resolver/resolver_test.go @@ -147,6 +147,14 @@ func (m maliciousPattern) String() string { return "/" } +func (m maliciousPattern) ExpandID(secrets.ID) (secrets.ID, error) { + panic("implement me") +} + +func (m maliciousPattern) ExpandPattern(secrets.Pattern) (secrets.Pattern, error) { + panic("implement me") +} + func newGetSecretRequest(pattern secrets.Pattern) *connect.Request[resolverv1.GetSecretsRequest] { return connect.NewRequest(resolverv1.GetSecretsRequest_builder{ Pattern: proto.String(pattern.String()), diff --git a/x/secrets/pattern.go b/x/secrets/pattern.go index 62d77c23..46b43ede 100644 --- a/x/secrets/pattern.go +++ b/x/secrets/pattern.go @@ -2,6 +2,8 @@ package secrets import ( "errors" + "fmt" + "strings" ) var ErrInvalidPattern = errors.New("invalid pattern") @@ -71,6 +73,9 @@ type Pattern interface { Includes(other Pattern) bool // String formats the [Pattern] as a string String() string + + ExpandID(other ID) (ID, error) + ExpandPattern(other Pattern) (Pattern, error) } type pattern string @@ -118,6 +123,22 @@ func MustParsePattern(s string) Pattern { return pattern(s) } +func (p pattern) ExpandID(other ID) (ID, error) { + val, err := replace1(string(p), other.String()) + if err != nil { + return nil, err + } + return id(val), err +} + +func (p pattern) ExpandPattern(other Pattern) (Pattern, error) { + val, err := replace1(string(p), other.String()) + if err != nil { + return nil, err + } + return pattern(val), err +} + // Filter returns a reduced [Pattern] that is subset equal to [filter]. // Returns false if there's no overlap between [filter] and [other]. // Examples: @@ -134,3 +155,18 @@ func Filter(filter, other Pattern) (Pattern, bool) { } return nil, false } + +func replace1(original, other string) (string, error) { + components := split(original) + var candidates []int + for idx, val := range components { + if val == "**" { + candidates = append(candidates, idx) + } + } + if len(candidates) != 1 { + return "", fmt.Errorf("expand only supports one expansion glob, pattern %s has %d", original, len(candidates)) + } + components[candidates[0]] = other + return strings.Join(components, "/"), nil +} diff --git a/x/secrets/pattern_test.go b/x/secrets/pattern_test.go index 8796984a..8913817f 100644 --- a/x/secrets/pattern_test.go +++ b/x/secrets/pattern_test.go @@ -141,3 +141,72 @@ func Test_Filter(t *testing.T) { }) } } + +func Test_apply(t *testing.T) { + type query struct { + pattern string + result string + } + tests := []struct { + pattern string + queries []query + }{ + { + pattern: "foo/bar/**", + queries: []query{ + { + pattern: "baz", + result: "foo/bar/baz", + }, + { + pattern: "**", + result: "foo/bar/**", + }, + { + pattern: "**/*", + result: "foo/bar/**/*", + }, + }, + }, + { + pattern: "**/*", + queries: []query{ + { + pattern: "baz", + result: "baz/*", + }, + { + pattern: "bar/baz", + result: "bar/baz/*", + }, + { + pattern: "**", + result: "**/*", + }, + }, + }, + { + pattern: "**/**", + queries: []query{ + { + pattern: "**/bar", + }, + }, + }, + } + for idx, test := range tests { + t.Run(fmt.Sprintf("%d - %s", idx, test.pattern), func(t *testing.T) { + for inner, query := range test.queries { + t.Run(fmt.Sprintf("%d-%d %s %s", idx, inner, test.pattern, query.pattern), func(t *testing.T) { + result, err := replace1(test.pattern, query.pattern) + if query.result == "" { + assert.Error(t, err, fmt.Sprintf("got: %s", result)) + return + } + require.NoError(t, err, fmt.Sprintf("query: %s, expected out: %s", query.pattern, query.result)) + assert.Equal(t, query.result, result) + }) + } + }) + } +}