Skip to content

Commit a7a94d2

Browse files
committed
templates: add safeguard against building very long strings
1 parent ec9cf3f commit a7a94d2

2 files changed

Lines changed: 54 additions & 16 deletions

File tree

common/templates/general.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -364,32 +364,48 @@ func tmplRoundEven(args ...interface{}) float64 {
364364
return math.RoundToEven(ToFloat64(args[0]))
365365
}
366366

367-
func joinStrings(sep string, args ...interface{}) string {
367+
var ErrStringTooLong = errors.NewPlain("String is too long (max 1MB)")
368368

369-
out := ""
369+
const MaxStringLength = 1000000
370+
371+
func joinStrings(sep string, args ...interface{}) (string, error) {
372+
373+
var builder strings.Builder
370374

371375
for _, v := range args {
376+
if builder.Len() != 0 {
377+
builder.WriteString(sep)
378+
}
379+
372380
switch t := v.(type) {
381+
373382
case string:
374-
if out != "" {
375-
out += sep
376-
}
383+
builder.WriteString(t)
377384

378-
out += t
379385
case []string:
380-
for _, s := range t {
381-
if out != "" {
382-
out += sep
386+
for j, s := range t {
387+
if j != 0 {
388+
builder.WriteString(sep)
383389
}
384390

385-
out += s
391+
builder.WriteString(s)
392+
if builder.Len() > MaxStringLength {
393+
return "", ErrStringTooLong
394+
}
386395
}
396+
387397
case int, int32, uint32, int64, uint64:
388-
out += ToString(v)
398+
builder.WriteString(ToString(v))
399+
389400
}
401+
402+
if builder.Len() > MaxStringLength {
403+
return "", ErrStringTooLong
404+
}
405+
390406
}
391407

392-
return out
408+
return builder.String(), nil
393409
}
394410

395411
func sequence(start, stop int) ([]int, error) {

common/templates/general_test.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,45 @@ package templates
33
import (
44
"reflect"
55
"strconv"
6+
"strings"
67
"testing"
78
)
89

10+
func buildLongStr(length int) string {
11+
var b strings.Builder
12+
13+
for i := 0; i < length; i++ {
14+
b.WriteString("A")
15+
}
16+
17+
return b.String()
18+
}
19+
920
func TestJoinStrings(t *testing.T) {
21+
22+
longString := buildLongStr(1000000)
23+
1024
cases := []struct {
1125
sep string
1226
args []interface{}
1327
expectedResult string
28+
shouldError bool
1429
}{
15-
{" ", []interface{}{"hello", "world"}, "hello world"},
16-
{",", []interface{}{"hello", []string{"world", "!"}}, "hello,world,!"},
17-
{" ", []interface{}{[]string{"hello", "world", "!"}}, "hello world !"},
30+
{" ", []interface{}{"hello", "world"}, "hello world", false},
31+
{" ", []interface{}{"hello", "world", longString}, "", true},
32+
{",", []interface{}{"hello", []string{"world", "!"}}, "hello,world,!", false},
33+
{" ", []interface{}{[]string{"hello", "world", "!"}}, "hello world !", false},
34+
{" ", []interface{}{[]string{"hello", "world", "!", longString}}, "", true},
1835
}
1936

2037
for i, c := range cases {
2138
t.Run("case #"+strconv.Itoa(i), func(t *testing.T) {
22-
joined := joinStrings(c.sep, c.args...)
39+
joined, err := joinStrings(c.sep, c.args...)
40+
if err != nil && !c.shouldError {
41+
t.Errorf("Should not have errored out")
42+
} else if err == nil && c.shouldError {
43+
t.Errorf("Should have errored out")
44+
}
2345
if joined != c.expectedResult {
2446
t.Error("Unexpected result, got ", joined, ", expected ", c.expectedResult)
2547
}

0 commit comments

Comments
 (0)