-
-
Notifications
You must be signed in to change notification settings - Fork 278
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
Improve support for template/html.HTML type #175
Comments
Might not be the proper solution, but you can explicitly cast it to a string to resolve the error: templ SomePage(h template.HTML) {
<div>
{ string(h) }
</div>
} This doesn't appear to be an issue with templ, but just how Go's type system works. You get the same error with just Go: func foo(h template.HTML) {
var s string = h
} |
The problem with this is it escapes the html and puts it into the HTML as a string The Would be handy to have something similar to |
This might be a good feature to add. In terms of how this could work with templ, then I think it's just a templ component that renders the func GoTemplate(h template.HTML) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
_, err := io.WriteString(w, string(h))
return err
})
} You could then use this in your templ code as:
The only difference if it was built into templ would be that it would be a call to:
So far, I've resisted adding something built-in to templ to render unsafely since I don't want people to shoot themselves in the foot, but a more general version would bypass templ's output escaping for all strings: func DangerouslyIncludeHTML(s string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
_, err := io.WriteString(w, s)
return err
})
} The question is whether to provide a "Dangerous..." component, and if so, whether to bother with a specific function for Go HTML templates OR, to provide one for Go HTML template values only. And for both, the classic problem of what they should be called. 😁 |
I would find a SafeHTML function, similar to SafeURL, that strips script tags etc very useful to handle untrusted html. |
Are you thinking something along the lines of:
|
This suggested solution worked for me and I switched to it for now until a feautre in templ itself lands |
The safe variants already live in the templ package. |
The concept of safety that we're talking about is whether the content is fully under control of the developer. Dynamic content from external sources (user input, 3rd party libraries etc.) isn't under our control, so we shouldn't trust it. It's fairly well explained at https://gohugo.io/functions/safe/url/ and https://gohugo.io/functions/safe/html/ docs in short form, and in the linked OWASP video at https://github.com/google/safehtml The The idea is that all content goes through the sanitization and escaping process, unless it is a In the case of templ, adding a // SafeHTML is HTML that has come from a trusted source, or has been sanitized.
type SafeHTML string
// Include HTML from another trusted source.
func Include(s SafeHTML) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
_, err := io.WriteString(w, string(s))
return err
})
} Then, it would be a case of:
Could also echo Google's SafeHTML approach which prevents type casting. // SafeHTML is HTML that has come from a trusted source, or has been sanitized.
type SafeHTML struct {
content string
}
func DeclareHTMLSafe(s string) SafeHTML {
return SafeHTML{ content: s }
}
// Include HTML from another trusted source.
func Include(s SafeHTML) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
_, err := io.WriteString(w, s.content)
return err
})
}
|
Hi @a-h you're right, I got confused, ignore my remarks above. |
If we focus back on CSRF token handling, then we don't need a Safe HTML thing at all. Note the
Then, in the HTTP handler, it's a case of passing the func getHandler(w http.ResponseWriter, r *http.Request) {
userCount := sessionManager.GetInt(r.Context(), "count")
component := page(csrf.Token(r), global.Count, userCount)
component.Render(r.Context(), w)
} So. I think we have two things in this issue.
I think the answer to #1 is no. Not having it makes accidental XSS much less likely, and I think that anyone who knows they need it won't mind writing the few lines of code. It could be documented though. On 2, I think a docs update would be a good idea. I think a new section of "Using with other tools", and having "Gorilla", "HTMX" etc. under that would make sense. So... actions are:
|
Right now, I have created a helper function I use for this func Raw(s string, errs ...error) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
_, err := fmt.Fprint(w, s)
return errors.Join(append(errs, err)...)
})
} I really think the ability to render raw HTML should be part of the package itself. It makes sense to give the user a convenient way to opt-out of HTML escaping. |
The ability to render raw HTML should at least be mentioned in the documentation if its not included. I don't mind adding in It's totally understandable if you don't want to include it, but its also one of those things that is required unfortunately, hence appearing in every template language out there. BTW thanks so much for making this. I absolutely adore it and plan on porting everything I maintain to it over time. |
Thanks for this repo and the helper func. However, I agree with @boyter: this really should be included in the docs if it's not available and point to this issue. I had never had problem with raw HTML for frontend before until using |
Makes sense, I propose that we add the following... // Raw renders the input HTML to the output without applying HTML escaping.
//
// Use of this component presents a security risk - the HTML should come from
// a trusted source, because it will be included as-is in the output.
func Raw[T ~string](html T, errs ...error) Component {
return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
if err = errors.Join(errs...); err != nil {
return err
}
_, err = io.WriteString(w, string(html))
return err
})
}
// GoHTMLTemplate renders the Go html/template to the output.
func GoHTMLTemplate(t *template.Template, data any) Component {
return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
return t.Execute(w, data)
})
} I've got tests for these components ready, and will add some documentation alongside. |
OK folks, PR is in place. Comments welcome. I updated it to add bi-directional support - with these helper functions you can use templ in Go templates, and Go templates in templ. |
I just wanted to leave an update on using Gorilla CSRF with templ for anyone that got here for the same reason. I believe we can achieve the same things a bit easier. All csrf.TemplateField and csrf.Token does is look for "gorilla.csrf.Token" key on the request context. So you can just do this instead:
and use that template inside any form. You wont need to drill the token from the handler. In my case is working without an issue just make sure to have added the csrf middleware on your http Handler. @a-h if its ok, i'm happy to make a pr adding a page to the docs about working with Gorilla CSRF including your examples |
Thanks @xV0lk - if you have time for a PR, that would be great! |
I'm using the https://github.com/gorilla/csrf library to get a csrf token for forms and it provides you with a https://pkg.go.dev/html/template#HTML when you use the https://pkg.go.dev/github.com/gorilla/csrf#TemplateField func
This all works fine as
HTML
is just a string but right now the compiler does give a warning or something?A really simple reproducible example is
Which generates the following
Templ Version
I would try do this but I'm still getting to know the library and how it all works so I thought I'd just log it here in case anyone else with more experience can quickly solve this
The text was updated successfully, but these errors were encountered: