mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
fix(codersdk): reject trailing data after closing single quote in env import (#27474)
This commit is contained in:
@@ -191,11 +191,7 @@ func parseEnvValue(rhs string, lineNum int) (string, error) {
|
||||
}
|
||||
return unescapeDoubleQuoted(inner), nil
|
||||
case '\'':
|
||||
inner, ok := quotedInner(v, '\'')
|
||||
if !ok {
|
||||
return "", xerrors.Errorf("line %d: missing closing single quote", lineNum)
|
||||
}
|
||||
return inner, nil
|
||||
return singleQuotedInner(v, lineNum)
|
||||
default:
|
||||
return strings.TrimSpace(v), nil
|
||||
}
|
||||
@@ -222,12 +218,21 @@ func hasOddBackslashRun(s string, before int) bool {
|
||||
return count%2 == 1
|
||||
}
|
||||
|
||||
func quotedInner(v string, quote byte) (string, bool) {
|
||||
trimmed := strings.TrimRight(v, " \t")
|
||||
if len(trimmed) < 2 || trimmed[len(trimmed)-1] != quote {
|
||||
return "", false
|
||||
// singleQuotedInner returns the content between the opening single
|
||||
// quote at index 0 and the first closing single quote. Single quotes
|
||||
// have no escape sequences, so the first quote after the opener always
|
||||
// closes the value. Only whitespace may follow the closing quote.
|
||||
func singleQuotedInner(v string, lineNum int) (string, error) {
|
||||
for i := 1; i < len(v); i++ {
|
||||
if v[i] != '\'' {
|
||||
continue
|
||||
}
|
||||
if strings.Trim(v[i+1:], " \t") != "" {
|
||||
return "", xerrors.Errorf("line %d: unexpected data after closing single quote", lineNum)
|
||||
}
|
||||
return v[1:i], nil
|
||||
}
|
||||
return trimmed[1 : len(trimmed)-1], true
|
||||
return "", xerrors.Errorf("line %d: missing closing single quote", lineNum)
|
||||
}
|
||||
|
||||
func unescapeDoubleQuoted(s string) string {
|
||||
|
||||
@@ -29,6 +29,11 @@ func TestParseSecretsFileEnv(t *testing.T) {
|
||||
`DQ_CARRIAGE="a\rb"`,
|
||||
`DQ_UNKNOWN="x\zy"`,
|
||||
`SQUOTED='literal \n no escape'`,
|
||||
"SQ_TRAILING_WS='abc' \t",
|
||||
"SQ_EMPTY=''",
|
||||
`SQ_DOUBLE_INSIDE='a"b'`,
|
||||
`SQ_HASH='#not a comment'`,
|
||||
"SQ_UNICODE='héllo 世界'",
|
||||
"EQ_IN_VALUE=a=b=c",
|
||||
"HASH=value # kept literal",
|
||||
"UNICODE=héllo 世界 café",
|
||||
@@ -54,6 +59,11 @@ func TestParseSecretsFileEnv(t *testing.T) {
|
||||
{Name: "DQ_CARRIAGE", EnvName: "DQ_CARRIAGE", Value: "a\rb"},
|
||||
{Name: "DQ_UNKNOWN", EnvName: "DQ_UNKNOWN", Value: `x\zy`},
|
||||
{Name: "SQUOTED", EnvName: "SQUOTED", Value: `literal \n no escape`},
|
||||
{Name: "SQ_TRAILING_WS", EnvName: "SQ_TRAILING_WS", Value: "abc"},
|
||||
{Name: "SQ_EMPTY", EnvName: "SQ_EMPTY", Value: ""},
|
||||
{Name: "SQ_DOUBLE_INSIDE", EnvName: "SQ_DOUBLE_INSIDE", Value: `a"b`},
|
||||
{Name: "SQ_HASH", EnvName: "SQ_HASH", Value: "#not a comment"},
|
||||
{Name: "SQ_UNICODE", EnvName: "SQ_UNICODE", Value: "héllo 世界"},
|
||||
{Name: "EQ_IN_VALUE", EnvName: "EQ_IN_VALUE", Value: "a=b=c"},
|
||||
{Name: "HASH", EnvName: "HASH", Value: "value # kept literal"},
|
||||
{Name: "UNICODE", EnvName: "UNICODE", Value: "héllo 世界 café"},
|
||||
@@ -92,7 +102,15 @@ func TestParseSecretsFileEnvErrors(t *testing.T) {
|
||||
{name: "UnterminatedDouble", content: `KEY="oops`, errMsgs: []string{"missing closing double quote"}},
|
||||
{name: "EscapedDoubleQuoteNotClosing", content: `KEY="oops\"`, errMsgs: []string{"missing closing double quote"}},
|
||||
{name: "DoubleQuoteTrailingData", content: `KEY="ok" # comment`, errMsgs: []string{"unexpected data after closing double quote"}},
|
||||
{name: "UnterminatedSingle", content: `KEY='oops`, errMsgs: []string{"missing closing single quote"}},
|
||||
{name: "UnterminatedSingle", content: `KEY='oops`, errMsgs: []string{"missing closing single quote", "line 1"}},
|
||||
{name: "SingleQuoteOnly", content: `KEY='`, errMsgs: []string{"missing closing single quote", "line 1"}},
|
||||
{name: "SingleQuoteTrailingComment", content: `KEY='abc' # 'note'`, errMsgs: []string{"unexpected data after closing single quote", "line 1"}},
|
||||
{name: "SingleQuoteEmbeddedQuote", content: `KEY='a'b'`, errMsgs: []string{"unexpected data after closing single quote", "line 1"}},
|
||||
{name: "SingleQuotePairAfterClose", content: `KEY='a' 'b'`, errMsgs: []string{"unexpected data after closing single quote", "line 1"}},
|
||||
{name: "SingleQuoteTrailingData", content: `KEY='abc' extra`, errMsgs: []string{"unexpected data after closing single quote", "line 1"}},
|
||||
{name: "SingleQuoteAdjacentQuoted", content: `KEY='it''s'`, errMsgs: []string{"unexpected data after closing single quote", "line 1"}},
|
||||
{name: "SingleQuoteTrailingDataNoSpace", content: `KEY=''extra`, errMsgs: []string{"unexpected data after closing single quote", "line 1"}},
|
||||
{name: "SingleQuoteErrorOnLaterLine", content: "OK=fine\nKEY='abc' extra", errMsgs: []string{"unexpected data after closing single quote", "line 2"}},
|
||||
{name: "DuplicateKey", content: "DUP=a\nDUP=b", errMsgs: []string{"duplicate key", "line 2"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -254,6 +272,7 @@ func FuzzParseSecretsFile(f *testing.F) {
|
||||
f.Add("env", "NOEQUALS")
|
||||
f.Add("env", "=value")
|
||||
f.Add("env", `KEY="ok" # trailing`)
|
||||
f.Add("env", `KEY='ok' extra`)
|
||||
f.Add("env", "DUP=a\nDUP=b")
|
||||
// json - valid
|
||||
f.Add("json", `{"A":"1","B":"two"}`)
|
||||
|
||||
Reference in New Issue
Block a user