Security Report: Multiple Vulnerabilities in sprig v3.3.0
Note: This repository has no security policy (SECURITY.md) and private vulnerability reporting is not enabled. This issue is filed publicly as there is no private channel available for responsible disclosure.
Tested on sprig v3.3.0 with Go 1.25.6.
Finding 1 (HIGH): Unbounded Resource Allocation DoS — CWE-770
Affected functions: repeat, until, untilStep, seq, indent, randAlphaNum, randAlpha, randBytes
These functions accept unbounded size/count parameters. When user-controlled input is passed as the size argument, it directly controls memory allocation with no upper bounds, enabling trivial denial-of-service.
Reproduction:
| Template |
Allocation |
{{ "X" | repeat 100000000 }} |
~95 MB |
{{ until 50000000 }} |
~2.2 GB |
{{ seq 1 10000000 }} |
~1.3 GB |
{{ randAlpha 50000000 }} |
~4.2 GB |
Root cause: functions.go line 128 — strings.Repeat(str, count) with no upper bound on count. The same unbounded pattern applies to until, seq, and the rand* family.
Suggested fix: Add configurable upper bounds on count/size parameters. Consider a MaxAlloc option or similar mechanism.
Finding 2 (MEDIUM): AES-CBC Padding Oracle with Error-Based Information Leak — CWE-327, CWE-347
Affected functions: encryptAES, decryptAES
encryptAES uses AES-CBC mode without any authentication (no HMAC, no GCM). decryptAES at crypto.go line 658 uses the last decrypted byte as the padding length without any validation:
return string(decrypted[:len(decrypted)-int(decrypted[len(decrypted)-1])]), nil
When decryption with a wrong key produces a last byte value N > len(decrypted), Go's slice bounds check panics with "slice bounds out of range [:-N]", leaking the decrypted byte value N through the error message.
Additionally, key derivation at lines 608–609 is just zero-padding:
key := make([]byte, 32)
copy(key, []byte(password))
No KDF, no salt, no iterations — passwords are used directly as key material.
Suggested fix: Replace AES-CBC with AES-GCM (authenticated encryption). Add proper PKCS#7 padding validation. Use a real KDF (e.g., Argon2, scrypt, or at minimum PBKDF2).
Finding 3 (MEDIUM): getHostByName Crash on Failed DNS Lookup — CWE-476
Affected function: getHostByName
In network.go lines 8–12:
func getHostByName(name string) string {
addrs, _ := net.LookupHost(name)
//TODO: add error handing when release v3 comes out
return addrs[rand.Intn(len(addrs))]
}
The error from net.LookupHost is discarded. When DNS resolution fails, addrs is nil, and rand.Intn(0) panics with an unrecoverable crash. The TODO comment in the source code acknowledges this was supposed to be fixed in v3 but never was.
Suggested fix: Check the error return value and handle the empty addrs slice (return empty string or propagate error).
Finding 4 (LOW–MEDIUM): Error Messages Rendered as Template Output — CWE-209
Affected functions: htpasswd, bcrypt, b64dec, b32dec, genPrivateKey, derivePassword
These functions return Go error messages as template string output rather than returning errors through Go's template error mechanism. For example, {{ b64dec "!!!" }} outputs "illegal base64 data at input byte 0" as rendered content.
Root cause: strings.go lines 18–24 — return err.Error() instead of propagating the error properly. This can leak internal implementation details and Go runtime information to end users.
Suggested fix: Return errors through the template error channel rather than rendering them as output strings.
Impact on Downstream Projects
Sprig is a widely-used template function library consumed by:
- Helm (~27k stars) — Kubernetes package manager
- Hugo — Static site generator
- Listmonk — Mailing list manager
- Many other Go projects
While Helm removes env/expandenv, all DoS-capable functions (repeat, until, seq, randAlpha, etc.) remain available in Helm templates. Any system that renders user-supplied or partially-user-controlled templates using sprig is affected.
Recommendations
- Enable private vulnerability reporting on this repository via Settings → Code security and analysis → Private vulnerability reporting.
- Add a
SECURITY.md with disclosure instructions.
- Address the findings above, prioritizing Finding 1 (DoS) and Finding 3 (panic) as they have the most straightforward exploitation path.
Security Report: Multiple Vulnerabilities in sprig v3.3.0
Tested on sprig v3.3.0 with Go 1.25.6.
Finding 1 (HIGH): Unbounded Resource Allocation DoS — CWE-770
Affected functions:
repeat,until,untilStep,seq,indent,randAlphaNum,randAlpha,randBytesThese functions accept unbounded size/count parameters. When user-controlled input is passed as the size argument, it directly controls memory allocation with no upper bounds, enabling trivial denial-of-service.
Reproduction:
{{ "X" | repeat 100000000 }}{{ until 50000000 }}{{ seq 1 10000000 }}{{ randAlpha 50000000 }}Root cause:
functions.goline 128 —strings.Repeat(str, count)with no upper bound oncount. The same unbounded pattern applies tountil,seq, and therand*family.Suggested fix: Add configurable upper bounds on count/size parameters. Consider a
MaxAllocoption or similar mechanism.Finding 2 (MEDIUM): AES-CBC Padding Oracle with Error-Based Information Leak — CWE-327, CWE-347
Affected functions:
encryptAES,decryptAESencryptAESuses AES-CBC mode without any authentication (no HMAC, no GCM).decryptAESatcrypto.goline 658 uses the last decrypted byte as the padding length without any validation:When decryption with a wrong key produces a last byte value
N > len(decrypted), Go's slice bounds check panics with"slice bounds out of range [:-N]", leaking the decrypted byte valueNthrough the error message.Additionally, key derivation at lines 608–609 is just zero-padding:
No KDF, no salt, no iterations — passwords are used directly as key material.
Suggested fix: Replace AES-CBC with AES-GCM (authenticated encryption). Add proper PKCS#7 padding validation. Use a real KDF (e.g., Argon2, scrypt, or at minimum PBKDF2).
Finding 3 (MEDIUM): getHostByName Crash on Failed DNS Lookup — CWE-476
Affected function:
getHostByNameIn
network.golines 8–12:The error from
net.LookupHostis discarded. When DNS resolution fails,addrsisnil, andrand.Intn(0)panics with an unrecoverable crash. The TODO comment in the source code acknowledges this was supposed to be fixed in v3 but never was.Suggested fix: Check the error return value and handle the empty
addrsslice (return empty string or propagate error).Finding 4 (LOW–MEDIUM): Error Messages Rendered as Template Output — CWE-209
Affected functions:
htpasswd,bcrypt,b64dec,b32dec,genPrivateKey,derivePasswordThese functions return Go error messages as template string output rather than returning errors through Go's template error mechanism. For example,
{{ b64dec "!!!" }}outputs"illegal base64 data at input byte 0"as rendered content.Root cause:
strings.golines 18–24 —return err.Error()instead of propagating the error properly. This can leak internal implementation details and Go runtime information to end users.Suggested fix: Return errors through the template error channel rather than rendering them as output strings.
Impact on Downstream Projects
Sprig is a widely-used template function library consumed by:
While Helm removes
env/expandenv, all DoS-capable functions (repeat, until, seq, randAlpha, etc.) remain available in Helm templates. Any system that renders user-supplied or partially-user-controlled templates using sprig is affected.Recommendations
SECURITY.mdwith disclosure instructions.