Skip to content

Commit d82b6d0

Browse files
authored
Fix keystore secrets parsing when values contain commas. (#50)
1 parent b57c509 commit d82b6d0

File tree

6 files changed

+31
-11
lines changed

6 files changed

+31
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build/

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.17
55
require (
66
github.com/Microsoft/go-winio v0.5.2
77
github.com/elastic/go-structform v0.0.9
8-
github.com/elastic/go-ucfg v0.8.4
8+
github.com/elastic/go-ucfg v0.8.5
99
github.com/fatih/color v1.13.0
1010
github.com/hashicorp/go-multierror v1.1.1
1111
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ github.com/elastic/go-structform v0.0.9 h1:HpcS7xljL4kSyUfDJ8cXTJC6rU5ChL1wYb6cx
104104
github.com/elastic/go-structform v0.0.9/go.mod h1:CZWf9aIRYY5SuKSmOhtXScE5uQiLZNqAFnwKR4OrIM4=
105105
github.com/elastic/go-sysinfo v1.7.1 h1:Wx4DSARcKLllpKT2TnFVdSUJOsybqMYCNQZq1/wO+s0=
106106
github.com/elastic/go-sysinfo v1.7.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0=
107-
github.com/elastic/go-ucfg v0.8.4 h1:OAHTnubzXKsYYYWVzl8psLcS5mCbNKjXxtMY41itthk=
108-
github.com/elastic/go-ucfg v0.8.4/go.mod h1:4E8mPOLSUV9hQ7sgLEJ4bvt0KhMuDJa8joDT2QGAEKA=
107+
github.com/elastic/go-ucfg v0.8.5 h1:4GB/rMpuh7qTcSFaxJUk97a/JyvFzhi6t+kaskTTLdM=
108+
github.com/elastic/go-ucfg v0.8.5/go.mod h1:4E8mPOLSUV9hQ7sgLEJ4bvt0KhMuDJa8joDT2QGAEKA=
109109
github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=
110110
github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0=
111111
github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss=

keystore/file_keystore_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ import (
3131
)
3232

3333
var keyValue = "output.elasticsearch.password"
34-
var secretValue = []byte("secret")
34+
35+
// Include uppercase, lowercase, symbols, and whitespace in the password.
36+
// Commas in particular have caused parsing issues before: https://github.com/elastic/beats/issues/29789
37+
var secretValue = []byte(",s3cRet~`! @#$%^&*()_-+={[}]|\\:;\"'<,>.?/")
3538

3639
func TestCanCreateAKeyStore(t *testing.T) {
3740
path := GetTemporaryKeystoreFile()
@@ -229,7 +232,7 @@ func TestGetConfig(t *testing.T) {
229232

230233
secret, err := cfg.String("output.elasticsearch.password", 0)
231234
require.NoError(t, err)
232-
require.Equal(t, secret, "secret")
235+
require.Equal(t, string(secretValue), secret)
233236

234237
port, err := cfg.String("super.nested", 0)
235238
require.NoError(t, err)

keystore/keystore.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ type ListingKeystore interface {
7373
List() ([]string, error)
7474
}
7575

76+
// Use parse.NoopConfig to disable interpreting all parser characters when loading secrets.
77+
var parseConfig = parse.NoopConfig
78+
7679
// ResolverWrap wrap a config resolver around an existing keystore.
7780
func ResolverWrap(keystore Keystore) func(string) (string, parse.Config, error) {
7881
return func(keyName string) (string, parse.Config, error) {
@@ -82,17 +85,17 @@ func ResolverWrap(keystore Keystore) func(string) (string, parse.Config, error)
8285
// If we cannot find the key, its a non fatal error
8386
// and we pass to other resolver.
8487
if errors.Is(err, ErrKeyDoesntExists) {
85-
return "", parse.DefaultConfig, ucfg.ErrMissing
88+
return "", parseConfig, ucfg.ErrMissing
8689
}
87-
return "", parse.DefaultConfig, err
90+
return "", parseConfig, err
8891
}
8992

9093
v, err := key.Get()
9194
if err != nil {
92-
return "", parse.DefaultConfig, err
95+
return "", parseConfig, err
9396
}
9497

95-
return string(v), parse.DefaultConfig, nil
98+
return string(v), parseConfig, nil
9699
}
97100
}
98101

keystore/keystore_test.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"testing"
2323

2424
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/require"
2526

2627
ucfg "github.com/elastic/go-ucfg"
2728
"github.com/elastic/go-ucfg/parse"
@@ -47,6 +48,18 @@ func TestResolverWhenTheKeyExist(t *testing.T) {
4748
resolver := ResolverWrap(keystore)
4849
v, pCfg, err := resolver("output.elasticsearch.password")
4950
assert.NoError(t, err)
50-
assert.Equal(t, pCfg, parse.DefaultConfig)
51-
assert.Equal(t, v, "secret")
51+
require.NoError(t, err)
52+
require.Equal(t, pCfg, parseConfig)
53+
54+
// Regression test for https://github.com/elastic/beats/issues/29789
55+
// Cheat a bit by reproducing part of the go-ucfg dynamic variable resolution process here. The
56+
// config returned by the resolver will be used with a call to the go-ucfg parser. The
57+
// public entrypoint is the ValueWithConfig function below. Make sure the parsed value is
58+
// correct. See https://github.com/elastic/go-ucfg/blob/fc880abbe1f30b653d113da96a4a7e82743c0cc1/types.go#L539
59+
iface, err := parse.ValueWithConfig(v, pCfg)
60+
require.NoError(t, err)
61+
62+
secret, ok := iface.(string)
63+
require.True(t, ok, "parsed secret is not a string")
64+
require.Equal(t, string(secretValue), secret)
5265
}

0 commit comments

Comments
 (0)