diff --git a/crypto.go b/crypto.go index b63623e..17f0d5b 100644 --- a/crypto.go +++ b/crypto.go @@ -95,6 +95,7 @@ func htpasswd(username string, password string, hashAlgorithm HashAlgorithm) str } func randBytes(count int) (string, error) { + count = clampInt(count, maxAllocSize) buf := make([]byte, count) if _, err := rand.Read(buf); err != nil { return "", err diff --git a/functions.go b/functions.go index cda47d2..19aa504 100644 --- a/functions.go +++ b/functions.go @@ -125,7 +125,7 @@ var genericMap = map[string]interface{}{ "untitle": untitle, "substr": substring, // Switch order so that "foo" | repeat 5 - "repeat": func(count int, str string) string { return strings.Repeat(str, count) }, + "repeat": func(count int, str string) string { return strings.Repeat(str, clampInt(count, maxAllocSize)) }, // Deprecated: Use trimAll. "trimall": func(a, b string) string { return strings.Trim(b, a) }, // Switch order so that "$foo" | trimall "$" diff --git a/limits.go b/limits.go new file mode 100644 index 0000000..f74955d --- /dev/null +++ b/limits.go @@ -0,0 +1,16 @@ +package sprig + +// maxAllocSize is the maximum number of elements or bytes that template +// functions are allowed to allocate in a single call. This prevents +// denial-of-service when user-controlled input is passed as a size parameter. +const maxAllocSize = 1 << 20 // 1 MiB / ~1 million elements + +func clampInt(n, max int) int { + if n < 0 { + n = 0 + } + if n > max { + return max + } + return n +} diff --git a/numeric.go b/numeric.go index f68e418..2dc8e7e 100644 --- a/numeric.go +++ b/numeric.go @@ -65,6 +65,11 @@ func minf(a interface{}, i ...interface{}) float64 { } func until(count int) []int { + if count > maxAllocSize { + count = maxAllocSize + } else if count < -maxAllocSize { + count = -maxAllocSize + } step := 1 if count < 0 { step = -1 diff --git a/strings.go b/strings.go index e0ae628..448775b 100644 --- a/strings.go +++ b/strings.go @@ -56,22 +56,26 @@ func initials(s string) string { } func randAlphaNumeric(count int) string { + count = clampInt(count, maxAllocSize) // It is not possible, it appears, to actually generate an error here. r, _ := util.CryptoRandomAlphaNumeric(count) return r } func randAlpha(count int) string { + count = clampInt(count, maxAllocSize) r, _ := util.CryptoRandomAlphabetic(count) return r } func randAscii(count int) string { + count = clampInt(count, maxAllocSize) r, _ := util.CryptoRandomAscii(count) return r } func randNumeric(count int) string { + count = clampInt(count, maxAllocSize) r, _ := util.CryptoRandomNumeric(count) return r } @@ -107,6 +111,7 @@ func cat(v ...interface{}) string { } func indent(spaces int, v string) string { + spaces = clampInt(spaces, maxAllocSize) pad := strings.Repeat(" ", spaces) return pad + strings.Replace(v, "\n", "\n"+pad, -1) }