-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: text/template, html/template: add ExecuteContext methods #31107
Comments
Thanks for spearheading this. I wonder - is there a reason you chose names like |
It occurs to me that instead of (or perhaps as well as) adding a That way, it wouldn't be necessary to burden the template text itself with needing to know which functions expect a context argument, meaning context support could be more easily retrofitted without changing all external template text. |
@rogpeppe Regarding "the function calling code could recognize functions that take context.Context as a first argument, and automatically pass the context as that argument", we don't implicitly supply context during a function call anywhere in Go. Starting in text/template seems like an odd place. If we do add it, it seems like it should be explicit on use like everywhere else. ExecuteTemplate is a tiny wrapper around Execute. If we add a context-aware execute we can just use one, ExecuteContext, not also ExecuteContextTemplate. The built-in function |
Is there anywhere else that automatically turns an error-returning function into a function that doesn't appear to return an error? AFAIK that's a convenience provided by text/template but not elsewhere, which is why I thought its dual (context is in some senses dual to error, I think) might also be appropriate there. I can totally understand your argument too though. |
I agree with @rogpeppe but I have not a strong opinion on this as long as we provide an API to access context values from the templates. |
@rsc are you saying you're in favor of having a built-in |
The last time we discussed this we were focused on the propagation of context values to invoked functions. It still seems like that would be best done explicitly. Is it a common pattern today to do something like start a template with I don't understand the cancellation part. Is the idea that the template executor would occasionally poll the context to decide whether to stop early? Is template generation really so expensive that it would happen often enough that the result is no longer needed? How often would/should the template executor be checking? |
I haven't seen that pattern, but I've seen in a lot of places people taking some values out of
I tried putting some code into the cancellation part and it makes the implementation clunky and slower than I initially thought. Since I don't have a strong case for supporting cancellation I'm ok if we were to drop that part of the proposal. What I'm most concerned about is to have a way to easily propagate context values into templates so that it reduces friction for developers to get it right. I don't want people to have to read and pass N additional values at every Moreover if someone is already using a token and they want to add one more, they can just change the middleware and the templates if they use context as per this proposal. Otherwise they would need to change every call site of One concrete exampleLet's say I'm develping a web application and I use func RenderFoo(w http.ResponseWriter, r *http.Request) {
t.ExecuteTemplate(w, "foo.tmpl", map[string]interface{}{
csrf.TemplateTag: csrf.TemplateField(r),
"data": foo,
})
} Not only this is hard to read, it also makes templates a lot clunkier to write and use. If devs used to pass a struct in the template instead of writing Let's now say that I want to add strict CSP to my service. I need to find all calls to One might say that a team could just agree to never call NoteI work in Google security enhancement for the web. The way we deal with web security is to have developers write logic and templates and the security team owns part of the middleware and a wrapper around the templating library. func RenderFoo(w http.ResponseWriter, r *http.Request) {
t.ExecuteTemplateContext(r.Context(), w, "foo.tmpl", foo)
} And access data in the template with Since we are planning to open-source all the internal libraries to secure web applications in Go (starting from the html wrapper), it'd be nice to agree on a single way to do this kind of things. This would make it possible to have a non-fragmented ecosystem in Go web apps 😃 . |
To summarize the discussion so far, there have been at least four different things suggested:
It sounds like 4 is quite involved and probably not necessary. I'd like to hear from more template users to gauge whether having /cc @bep and feel free to add any other heavy template users. Thanks. |
For this to be really usable (and it really would be), you need to also do 3), which is also somehow in line with how you handle errors in templates (a method has 1 or 2 return values):
|
Looking at this feedback and considering the use case for it I'd then say that we could go for:
I'm not sure about a @rsc I'd say that this is still coherent with how we manage context in Go and idiomatic. Context must be passed to |
Automatically supplying the context as a first argument seems like a step too far, for the reasons I gave above (it's just not in keeping with Go being explicit about context, and why is context special?). I would feel more comfortable if we had a standard "template execution state" type that we wanted to make available to template functions and that was being passed automatically. Maybe the argument is to reuse context for that. I'm wary of overloading context too much though. I'm also wary of exposing too much template execution state (magic functions that rewrite variables etc seems like too much). If we only do (1) and (2), I understand it won't solve everyone's problems. Does it solve anyone's problems? How many people? |
Ho would this look? Do you have something in mind?
Context seemed to me like the best option to be coherent with request-scoped values and other affine concepts that are already in the standard library.
What do you mean by that? I was thinking about something way simpler, just to provide rendering-contextual values alongside with data to render.
If we had a way to pass and retrieve the context or something like it (potentially the data structure you were proposing above) we could build middleware or tooling to pre-process or transform templates before they are parsed. This would address the problem in the original report with very little more work than I thought. That said I don't know about other use cases, for example what @bep was referring to. |
@rsc said
A CSP nonce has effect because it appears in the response header, so it is scoped to the document as a whole. Most inputs to a template are properly scoped to a substring of the body, but one way to look at context is that it is special because it relates to a small amount of per-response or per-document scoped metadata. I'm not familiar enough with the internals to know there's a way to get auto-noncing without provisions for per-response scope, but in case you're not sold on the use case, nonce-based CSP is the way ISE recommends doing CSP.
In ISE we think it's a bad idea for template authors to get in the habit of sprinkling nonce attributes throughout templates. |
Given @mikesamuel's comment, it seems like all the discussion of context here would be creating the wrong solution to the problem. Perhaps instead we should be talking about how to let html/template automatically add the nonces? |
Hi, I'm the author of gomplate, where I found this issue when I was just now looking for a way to pass a I'd like to add my support for suggestion 1 and 3, namely:
I would also want cancellation support - some of my functions do lots of IO. As for a built-in And to address @rsc's concern about implicitly injecting a |
FWIW I am still a supporter of automatically passing a I think it's reasonable to have template functions that depend on some non-global state without the requirement for that state to be passed explicitly. This is a particular need when specifications change - if there is an existing corpus of templates that are using a function, there's currently no way to make that function depend on local execution state without breaking compatibility. Context is commonly used in Go to pass local state to wider scoped functions. For example, it's how request-scoped data is passed to handler-scoped HTTP methods. Providing a general mechanism to do that in |
What we've learned since #31107 (comment) is that the security arguments for context are likely spurious, and that nonces should be done differently. But other things may want context. I am still very skeptical of magically filling in an argument to a function based on invisible state. That would be the first time we've done that, as I said before. But we could still do what I mentioned above, namely:
(If you use the context template function without ExecuteContext, you'd get context.Background, presumably.) @rogpeppe, would that satisfy your need for context support? |
@rsc Not really, I'm afraid.. If I wanted to do that, it would be straightforward to add a The point is to be able to add contextual information to existing function calls without requiring the templates to change. The example in this issue is also a reasonable one, I think - it's nicer to be able to write |
I'm getting a feeling that I'm talking to deaf ears, but I will try one more time:
templateExecutor := templates.NewExecutor(funcs);
templateExecutor.Execute(templ, data); |
AIUI there was quite a bit more API involved than that, although it's a bit hard to tell without a specifically written-up proposal. I believe that total API surface area is an important consideration. If we can make the existing API work well enough for this use case, I'd consider that to be a solid argument in favour of that approach. |
How slow is it? Why? Does it really need to be that slow? I actually do this in some web servers I run and I've never noticed. But I'm sure it could be faster. |
This is a specifically written-up proposal.:
I suspect that your usage is "a template or two"? Doing this with hundreds of templates is something you will notice. Or, at least, your profiler/benchmarks should.
I have benchmarks showing >30% reduction in object allocation by stop doing the cloning (note at that number is from memory and approximate, and is from the total runtime of the application, not just this in isolation). There is also a time saver in there if you have lots of template function invocations (everything is a function there, so this is mostly always true) and avoid the current mutex. I could dig up those numbers, but I'm really busy in the next week or so.
Two main reasons:
I can add to 1) That there are some hidden data races in this area that I have not been able to reproduce in a standalone app.
No. If you remove the need to clone the templates to use a different set of functions, this goes away. |
I might be missing something, but @rogpeppe wrote,
but that's just not true. There is one way, maybe two. One is to pass the context as a field of the struct passed to Execute, in which case it's .Context whenever you need it. A simple wrapper for Execute could take care of that nicely. The other is that it might be possible to some trickery with the documented fact that although Funcs must be called before Parse, it is legal to update the elements of the FuncMap. I would like to understand what the real problem is here. There are several floating around: context is too hard, context is too explicit, context should be automatic but isn't. Which is it? |
How would you write a wrapper that takes an arbitrary The reason I originally opened this issue is that I need to pass some security nonces to the template and I don't want programmers to manually do that nor edit the templates to add the nonces in the right places (forgetting a nonce would be a vulnerability in some cases and a breakage in others). This means that our hardened library needs to rewrite the template before parse time and add more information at render time. I could simply not find a way to do this with the currently available API. |
(edited to fix; see comment below) With embedding I can do this to attach a Context to pre-existing data: https://play.golang.org/p/uupyX25iMpx
Output:
|
Sure, but it's not possible to "auto inject" that context into any method/function invocation the template is doing. I think @empijei sums this up nicely:
For me, it's less security and more convenience (this is a dynamic scripting language, many of the users are not programmers, so it's hard to get it right) and avoiding breaking/changing old APIs. |
I believe @robpike's example was meant to be https://play.golang.org/p/uupyX25iMpx
and output (which nicely demonstrates the importance of checking errors, especially in examples) |
It seems like the main point of contention is the "auto-injection" where a context is automatically inserted at function call arguments instead of having to be written in the template (as Maybe we should leave things alone - contexts, like errors, are plain data and can be treated as such. |
@rsc I would strongly suggest that if you're looking for consensus on a particular issue, you need to put some extra weight to the opinions from the people who actually use the package(s) in question for real-life applications. This is a general remark. |
@rsc would this be better if I rephrased this issue to be "proposal: provide a way to dynamically provide data to templates"? I am not particularly attached to the concept of context but I would like to have a way to write libraries for secure html templating without having to fork the I am fine with giving up on the context part but I would still like to be able to write wrappers to harden templates. Moreover it looks like @bep has a quite different use case that would also benefit from the ability of injecting data into a template. You convinced me that context is not the best way to address this, so what would you propose to do instead? It looks like achieving what I need is not possible with the current API. |
@empijei, it sounds like we should talk about what your exact requirements are. I agree that separating out the context suggestions from the security ones would help here. I'll reach out to you off-issue. @bep, I appreciate the needs users have, but we need to find a way to serve those without breaking the design of the package. Part of the design is that function invocations are understandable as Go function calls, and Go function calls have no parameters that are automatically filled in. Perhaps it would make sense to help make it clearer how to replace functions on a per-template basis, so that a function with the context pre-loaded could be installed easily. Would you like to suggest a change along those lines and file an issue for that? I think it's clear that there's not much agreement on this larger, combined issue. I hadn't seen proposal #36462 yet but I have added it to our incoming pile. |
If we can split out the security-needed changes to template from the hugo-needed changes to template I think that will make both discussions more productive. I suggest we file different issues for those two and close this one. |
@rsc Go function calls also does not have "auto handling" of error return values. I think I've said this before, but I'll repeat it once more: Go templates are reflection-based and highly dynamic in nature, have poor editor support (auto-completion, renames etc.) and have a large user base that falls in the "designer category" (i.e. not primarily programmers). This issue (and several other re. these packages, e.g. short-circuit of and/or, break out of loops) is just asking for ways to, on a practical level, do the same stuff that can be done in "regular Go", which calls for some level of pragmatism. |
@bep, thanks for the note. I suggest filing an issue specific to what you think Hugo users need. It sounds like this issue should be a likely decline, with work continuing on #38114 and any proposal that @bep files (including but maybe not limited to #36462). |
@rsc My remark was general advice; Hugo does not this particular issue and I have filed all relevant issues. |
No change in consensus here, so declined. Work continues on the other issues. |
This proposal is about providing a way to pass a
context.Context
to template execution.Summary
Reasons:
data interface{}
parameter, and this makes the package not flexible enough for some purposes (expecially for html web nonces).Advantages:
Detailed rationale
Cancellation
Templates might implement a rather complex logic, and they might import each other. It could be possible that given some specific conditions a template might run for a long time, even after the result output is not needed anymore.
Currently the only way to address this is to have a separate goroutine that closes the output io.Writer of the template.
This has three main downsides:
io.WriteCloser
does not document that callingClose
concurrently withWrite
is safe. This means that to cancel a template execution the programmer needs to wrap theirio.WriteCloser
in a safe one, and only then they can abort a template execution safelyio.Writer
they need to implement a custom closing mechanism on it.Contextual values
Sometimes data needs to be rendered differently based on the context it is rendered in.
In the following examples I'm referring to two
html/template
examples I struggled with:<form>
, I also need to add an anti-CSRF token. This means that the template might need a value in some cases, and might not need it in others. If a toolkit or framework wants to provide protection against CSRF attacks, it must require the users to add the CSRF field to all their data structures being rendered. If the programmer misses one, that becomes a vulnerability.<script>
or<style>
tag, they need to have a nonce in order to be executed when a strict Content Security Policy is enforced. This means that coders will have to manually pass the nonce value to all templates executing in their handlers. If a coder misses one, that breaks the service with a runtime error in the browser.It would be nice to be able to protect an entire
http.Handler
in ahttp.ServeMux
from CSRF and XSS and just add security tokens to the context. It would make it very hard for programmers to forget about security.Example
Adding context values will make HTML templates usage from requiring this:
to this (which also handles cancellation):
Toolkits and frameworks could then provide ways to pre-parse and transform templates and add values to requests context.
Backward compatibility
This change would be backward compatible as it is about adding two
func
in each package:(*Template).ExecuteTemplateContext(context.Context, io.Writer, string, interface{})
(*Template).ExecuteContext(context.Context, io.Writer, interface{})
That could add a
context
orctx
func to the defaultFuncMap
. This would also be backward compatible because no-one is usingExecuteContext
at the moment, and they could change their func names when migrating to this new API (only if they are using "context" as a name).Alternatively a forbidden name (like
!
or$_
) could be used, but I'm not a fan of this option.What do you think?
/cc @mvdan @esseks @mikesamuel @robpike @bradfitz @lweichselbaum
The text was updated successfully, but these errors were encountered: