liquid
liquid copied to clipboard
What kind of end user security does this implementation have?
Compared to the ruby implementation, does the go version have similar security/safety guarantees? What I mean is, liquid was meant for end-user modification and should not allow malicious code to be executed. Curious as to your comments in this regards.
Great work!
Here's what I can think of:
- The code, and the included filters and tags, don't access the disk or network.
- It is trivial to write a template that runs an infinite loop or consumes arbitrarily large CPU or RAM resources. If untrusted templates are run, this can be used to inject a DoS attack.
- It is trivial to write a template that runs on some data, but runs an infinite loop or consumes arbitrarily large CPU or RAM resources depending on the data. If data from untrusted sources is used, even on a trusted (but insufficiently audited) template, this can be used in a DoS attack.
- If you run any third-party tags, filters, or Droplet providers, you're running whatever Go code implements those extensions, which could of course do anything. You have to do some explicit programming work to compile or link such code into your program, of course.
- This code hasn't had an independent security audit, so user beware. There are likely to be vulnerabilities that I'm not aware of. I don't think the code base is particularly low-quality or insecure, but I also think that most unaudited code bases of similar size and complexity — especially code that implements interpreters, such as this program — are likely to have vulnerabilities.
Hi, first of all many thanks for the effort building this engine for Go, I very much appreciate it :) Now, I'm also using this in a public domain, i.e. end users are able to define their own templates. Of course DoS is a potential risk which must be tackled somehow in order to prevent server-side issues. Right now I do this via a timer + panic/recover, something like:
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
t := time.NewTimer(time.Second)
done := make(chan struct{})
go func() {
_, _ = schema.Liquid.ParseAndRenderString(tmpl.Template, map[string]any{
"order": order,
})
done <- struct{}{}
}()
select {
case <-t.C:
panic("render deadline exceeded")
case <-done:
t.Stop()
}
This works. However, it would be nice if I could handle this via the engine directly, e.g. by passing a context.Context
with a timeout to RenderWithContext
, by specifying an upper buffer size limit or by allowing to pass a custom io.Writer
to e.g. RenderF
. Do you have any thoughts on this?