Skip to content

Commit c3e7291

Browse files
committed
fix(envvar): expand only whole-value placeholders
Resolve env references only when a field's value is exactly ${VAR} or $VAR. A value that merely contains a $ (a password, a regex backreference, an unmatched brace) is now returned verbatim instead of being passed through os.Expand. This fixes two issues in secret fields (verificationToken, accessToken, webhook secret, source token, CLI token): - literal $ in a secret was reinterpreted as a variable reference, silently corrupting the secret when the embedded name matched a set env var, or failing startup when it did not. - malformed placeholders (${UNCLOSED, ${}) were silently truncated to a partial string or an empty value instead of being preserved.
1 parent ffb565c commit c3e7291

1 file changed

Lines changed: 28 additions & 25 deletions

File tree

engine/pkg/config/envvar/envvar.go

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
// Package envvar expands "${VAR}" and "$VAR" placeholders in selected config
2-
// fields using the process environment.
1+
// Package envvar expands a config field whose value is solely a "${VAR}" or
2+
// "$VAR" placeholder using the process environment. A value that merely
3+
// contains a "$" (a password, a regex backreference, an unmatched brace) is a
4+
// literal and is left unchanged.
35
package envvar
46

57
import (
68
"os"
9+
"regexp"
710

811
"github.com/pkg/errors"
912
)
@@ -14,35 +17,35 @@ type Field struct {
1417
Ptr *string
1518
}
1619

17-
// ExpandStrict expands "${VAR}" and "$VAR" references in s. It returns an
18-
// error if any referenced variable is unset, so misconfigured tokens fail
19-
// loudly at startup instead of silently resolving to an empty string.
20+
// placeholderRE matches a value that consists solely of a single "${VAR}" or
21+
// "$VAR" reference, capturing the variable name. Anything else (including a
22+
// value that merely embeds a "$") is treated as a literal so secrets that
23+
// legitimately contain a "$" survive load without corruption.
24+
var placeholderRE = regexp.MustCompile(`^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$|^\$([A-Za-z_][A-Za-z0-9_]*)$`)
25+
26+
// ExpandStrict resolves s when it is exactly a single "${VAR}" or "$VAR"
27+
// placeholder, returning the referenced environment variable's value. It
28+
// returns an error if the referenced variable is unset, so a misconfigured
29+
// placeholder fails loudly at startup instead of silently resolving to an
30+
// empty string. Any other value, including one that merely contains a "$", is
31+
// returned unchanged.
2032
func ExpandStrict(s string) (string, error) {
21-
if s == "" {
22-
return "", nil
33+
m := placeholderRE.FindStringSubmatch(s)
34+
if m == nil {
35+
return s, nil
2336
}
2437

25-
var missing string
26-
27-
resolved := os.Expand(s, func(name string) string {
28-
if missing != "" {
29-
return ""
30-
}
31-
32-
v, ok := os.LookupEnv(name)
33-
if !ok {
34-
missing = name
35-
return ""
36-
}
37-
38-
return v
39-
})
38+
name := m[1]
39+
if name == "" {
40+
name = m[2]
41+
}
4042

41-
if missing != "" {
42-
return "", errors.Errorf("environment variable %q is not set", missing)
43+
v, ok := os.LookupEnv(name)
44+
if !ok {
45+
return "", errors.Errorf("environment variable %q is not set", name)
4346
}
4447

45-
return resolved, nil
48+
return v, nil
4649
}
4750

4851
// ExpandFields applies ExpandStrict to each field and writes the resolved

0 commit comments

Comments
 (0)