-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Require braces to be present for environment variable expansion #1304
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,9 @@ func ChangeDefaultCfgfileFlag(beatName string) error { | |
|
||
path, err := filepath.Abs(filepath.Dir(os.Args[0])) | ||
if err != nil { | ||
return fmt.Errorf("Failed to set default config file location because the absolute path to %s could not be obtained. %v", os.Args[0], err) | ||
return fmt.Errorf("Failed to set default config file location because "+ | ||
"the absolute path to %s could not be obtained. %v", | ||
os.Args[0], err) | ||
} | ||
|
||
cliflag.DefValue = filepath.Join(path, beatName+".yml") | ||
|
@@ -80,12 +82,12 @@ func IsTestConfig() bool { | |
return *testConfig | ||
} | ||
|
||
// expandEnv replaces ${var} or $var in config according to the values of the | ||
// current environment variables. The replacement is case-sensitive. References | ||
// to undefined variables are replaced by the empty string. A default value | ||
// can be given by using the form ${var:default value}. | ||
// expandEnv replaces ${var} in config according to the values of the current | ||
// environment variables. The replacement is case-sensitive. References to | ||
// undefined variables are replaced by the empty string. A default value can be | ||
// given by using the form ${var:default value}. | ||
func expandEnv(config []byte) []byte { | ||
return []byte(os.Expand(string(config), func(key string) string { | ||
return []byte(expand(string(config), func(key string) string { | ||
keyAndDefault := strings.SplitN(key, ":", 2) | ||
key = keyAndDefault[0] | ||
|
||
|
@@ -103,3 +105,65 @@ func expandEnv(config []byte) []byte { | |
return v | ||
})) | ||
} | ||
|
||
// The following methods were copied from the os package of the stdlib. The | ||
// expand method was modified to only expand variables defined with braces and | ||
// ignore $var. | ||
|
||
// Expand replaces ${var} in the string based on the mapping function. | ||
func expand(s string, mapping func(string) string) string { | ||
buf := make([]byte, 0, 2*len(s)) | ||
// ${} is all ASCII, so bytes are fine for this operation. | ||
i := 0 | ||
for j := 0; j < len(s); j++ { | ||
if s[j] == '$' && j+2 < len(s) && s[j+1] == '{' { | ||
buf = append(buf, s[i:j]...) | ||
name, w := getShellName(s[j+1:]) | ||
buf = append(buf, mapping(name)...) | ||
j += w | ||
i = j + 1 | ||
} | ||
} | ||
return string(buf) + s[i:] | ||
} | ||
|
||
// isShellSpecialVar reports whether the character identifies a special | ||
// shell variable such as $*. | ||
func isShellSpecialVar(c uint8) bool { | ||
switch c { | ||
case '*', '#', '$', '@', '!', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
// isAlphaNum reports whether the byte is an ASCII letter, number, or underscore | ||
func isAlphaNum(c uint8) bool { | ||
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' | ||
} | ||
|
||
// getShellName returns the name that begins the string and the number of bytes | ||
// consumed to extract it. If the name is enclosed in {}, it's part of a ${} | ||
// expansion and two more bytes are needed than the length of the name. | ||
func getShellName(s string) (string, int) { | ||
switch { | ||
case s[0] == '{': | ||
if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' { | ||
return s[1:2], 3 | ||
} | ||
// Scan to closing brace | ||
for i := 1; i < len(s); i++ { | ||
if s[i] == '}' { | ||
return s[1:i], i + 1 | ||
} | ||
} | ||
return "", 1 // Bad syntax; just eat the brace. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we silently want to ignore bad syntax? |
||
case isShellSpecialVar(s[0]): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. checking conditions in caller, this case is not possible anymore. We're always in first case |
||
return s[0:1], 1 | ||
} | ||
// Scan alphanumerics. | ||
var i int | ||
for i = 0; i < len(s) && isAlphaNum(s[i]); i++ { | ||
} | ||
return s[:i], i | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,21 +46,31 @@ func TestExpandEnv(t *testing.T) { | |
in string | ||
out string | ||
}{ | ||
// Environment variables can be specified as ${env} or $env. | ||
{"x$y", "xy"}, | ||
{"x${y}", "xy"}, | ||
// Environment variables can be specified as ${env} only. | ||
{"${y}", "y"}, | ||
{"$y", "$y"}, | ||
|
||
// Environment variables are case-sensitive. Neither are replaced. | ||
{"x$Y", "x"}, | ||
{"x${Y}", "x"}, | ||
// Environment variables are case-sensitive. | ||
{"${Y}", ""}, | ||
|
||
// Defaults can only be specified when using braces. | ||
// Defaults can be specified. | ||
{"x${Z:D}", "xD"}, | ||
{"x${Z:A B C D}", "xA B C D"}, // Spaces are allowed in the default. | ||
{"x${Z:}", "x"}, | ||
|
||
// Defaults don't work unless braces are used. | ||
{"x$y:D", "xy:D"}, | ||
// Un-matched braces are swallowed by the Go os.Expand function. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This unfortunately increases the chance of it showing up accidentally, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it does. I'll see about modify their code to remove this behavior. I didn't realize it did this until I started reading the code. |
||
{"x${Y ${Z:Z}", "xZ"}, | ||
|
||
// Special environment variables are not replaced. | ||
{"$*", "$*"}, | ||
{"${*}", ""}, | ||
{"$@", "$@"}, | ||
{"${@}", ""}, | ||
{"$1", "$1"}, | ||
{"${1}", ""}, | ||
|
||
{"", ""}, | ||
{"$$", "$$"}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only other thing that might be useful is some sort of escaping. e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or optionally escape |
||
} | ||
|
||
for _, test := range tests { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only line that was changed from the
os.Expand
function.