From c1a65d589afd09c652759766d95bf7bc7354aaab Mon Sep 17 00:00:00 2001
From: Patrick Scheid
Date: Fri, 6 Jan 2023 14:46:12 +0100
Subject: [PATCH] Add protection for stack-overflows for nested keys
Signed-off-by: Patrick Scheid
---
pkg/strvals/literal_parser.go | 26 +++++++-----
pkg/strvals/literal_parser_test.go | 65 ++++++++++++++++++++++++++++++
2 files changed, 82 insertions(+), 9 deletions(-)
diff --git a/pkg/strvals/literal_parser.go b/pkg/strvals/literal_parser.go
index fcab0d891..f75655811 100644
--- a/pkg/strvals/literal_parser.go
+++ b/pkg/strvals/literal_parser.go
@@ -64,7 +64,7 @@ func newLiteralParser(sc *bytes.Buffer, data map[string]interface{}) *literalPar
func (t *literalParser) parse() error {
for {
- err := t.key(t.data)
+ err := t.key(t.data, 0)
if err == nil {
continue
}
@@ -89,7 +89,7 @@ func runesUntilLiteral(in io.RuneReader, stop map[rune]bool) ([]rune, rune, erro
}
}
-func (t *literalParser) key(data map[string]interface{}) (reterr error) {
+func (t *literalParser) key(data map[string]interface{}, nestedNameLevel int) (reterr error) {
defer func() {
if r := recover(); r != nil {
reterr = fmt.Errorf("unable to parse key: %s", r)
@@ -114,6 +114,12 @@ func (t *literalParser) key(data map[string]interface{}) (reterr error) {
return nil
case lastRune == '.':
+ // Check value name is within the maximum nested name level
+ nestedNameLevel++
+ if nestedNameLevel > MaxNestedNameLevel {
+ return fmt.Errorf("value name nested level is greater than maximum supported nested level of %d", MaxNestedNameLevel)
+ }
+
// first, create or find the target map in the given data
inner := map[string]interface{}{}
if _, ok := data[string(key)]; ok {
@@ -121,11 +127,13 @@ func (t *literalParser) key(data map[string]interface{}) (reterr error) {
}
// recurse on sub-tree with remaining data
- err := t.key(inner)
- if len(inner) == 0 {
+ err := t.key(inner, nestedNameLevel)
+ if err == nil && len(inner) == 0 {
return errors.Errorf("key map %q has no value", string(key))
}
- set(data, string(key), inner)
+ if len(inner) != 0 {
+ set(data, string(key), inner)
+ }
return err
case lastRune == '[':
@@ -143,7 +151,7 @@ func (t *literalParser) key(data map[string]interface{}) (reterr error) {
}
// now we need to get the value after the ]
- list, err = t.listItem(list, i)
+ list, err = t.listItem(list, i, nestedNameLevel)
set(data, kk, list)
return err
}
@@ -162,7 +170,7 @@ func (t *literalParser) keyIndex() (int, error) {
return strconv.Atoi(string(v))
}
-func (t *literalParser) listItem(list []interface{}, i int) ([]interface{}, error) {
+func (t *literalParser) listItem(list []interface{}, i, nestedNameLevel int) ([]interface{}, error) {
if i < 0 {
return list, fmt.Errorf("negative %d index not allowed", i)
}
@@ -196,7 +204,7 @@ func (t *literalParser) listItem(list []interface{}, i int) ([]interface{}, erro
}
// recurse
- err := t.key(inner)
+ err := t.key(inner, nestedNameLevel)
if err != nil {
return list, err
}
@@ -218,7 +226,7 @@ func (t *literalParser) listItem(list []interface{}, i int) ([]interface{}, erro
}
// Now we need to get the value after the ].
- list2, err := t.listItem(crtList, nextI)
+ list2, err := t.listItem(crtList, nextI, nestedNameLevel)
if err != nil {
return list, err
}
diff --git a/pkg/strvals/literal_parser_test.go b/pkg/strvals/literal_parser_test.go
index 1951ecaf4..4e74423d6 100644
--- a/pkg/strvals/literal_parser_test.go
+++ b/pkg/strvals/literal_parser_test.go
@@ -16,6 +16,7 @@ limitations under the License.
package strvals
import (
+ "fmt"
"testing"
"sigs.k8s.io/yaml"
@@ -413,3 +414,67 @@ func TestParseLiteralInto(t *testing.T) {
}
}
}
+
+func TestParseLiteralNestedLevels(t *testing.T) {
+ var keyMultipleNestedLevels string
+
+ for i := 1; i <= MaxNestedNameLevel+2; i++ {
+ tmpStr := fmt.Sprintf("name%d", i)
+ if i <= MaxNestedNameLevel+1 {
+ tmpStr = tmpStr + "."
+ }
+ keyMultipleNestedLevels += tmpStr
+ }
+
+ tests := []struct {
+ str string
+ expect map[string]interface{}
+ err bool
+ errStr string
+ }{
+ {
+ "outer.middle.inner=value",
+ map[string]interface{}{"outer": map[string]interface{}{"middle": map[string]interface{}{"inner": "value"}}},
+ false,
+ "",
+ },
+ {
+ str: keyMultipleNestedLevels + "=value",
+ err: true,
+ errStr: fmt.Sprintf("value name nested level is greater than maximum supported nested level of %d", MaxNestedNameLevel),
+ },
+ }
+
+ for _, tt := range tests {
+ got, err := ParseLiteral(tt.str)
+ if err != nil {
+ if tt.err {
+ if tt.errStr != "" {
+ if err.Error() != tt.errStr {
+ t.Errorf("Expected error: %s. Got error: %s", tt.errStr, err.Error())
+ }
+ }
+ continue
+ }
+ t.Fatalf("%s: %s", tt.str, err)
+ }
+
+ if tt.err {
+ t.Errorf("%s: Expected error. Got nil", tt.str)
+ }
+
+ y1, err := yaml.Marshal(tt.expect)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ y2, err := yaml.Marshal(got)
+ if err != nil {
+ t.Fatalf("Error serializing parsed value: %s", err)
+ }
+
+ if string(y1) != string(y2) {
+ t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2)
+ }
+ }
+}