html/templates - Replacing newlines with <br>
Solution 1
It seems you could run template.HTMLEscape() on your text first to sanitize it, then do the \n to
substitution that you trust, then use that as pre-escaped and trusted template data.
Update: Expanding on Kocka's example, this is what I had in mind:
package main
import (
"html/template"
"os"
"strings"
)
const page = `<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>{{.}}</p>
</body>
</html>`
const text = `first line
<script>dangerous</script>
last line`
func main() {
t := template.Must(template.New("page").Parse(page))
safe := template.HTMLEscapeString(text)
safe = strings.Replace(safe, "\n", "<br>", -1)
t.Execute(os.Stdout, template.HTML(safe)) // template.HTML encapsulates a known safe HTML document fragment.
}
http://play.golang.org/p/JiH0uD5Zh2
Output is
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>first line<br><script>dangerous</script><br>last line</p>
</body>
</html>
And text rendered in the browser is
first line
<script>dangerous</script>
last line
Solution 2
Not sure where you're substituting \n
for <br>
but if it's in go, you can cast the string as template.HTML
so it's not escaped.
See: http://golang.org/pkg/html/template/#HTML
If it's in a template, there should be a pipeline available, {{. | html}}
Solution 3
You can do it like this:
package main
import (
"html/template"
"os"
)
const page = `<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>{{.}}</p>
</body>
</html>`
func main() {
t := template.Must(template.New("page").Parse(page))
t.Execute(os.Stdout, template.HTML("<br>"))
}
Solution 4
The accepted answer can easily be turned into a custom template function:
func replaceNewline(s string) template.HTML {
return template.HTML(strings.Replace(template.HTMLEscapeString(s), "\n", "<br>", -1))
}
Add it with func (*Template) Funcs
Example
package main
import (
"html/template"
"os"
"strings"
)
const page = `<!DOCTYPE html>
<html>
<body>
<p>{{. | replaceNewline}}</p>
</body>
</html>`
const text = `first line
<script>dangerous</script>
last line`
func main() {
t := template.Must(template.New("page").Funcs(template.FuncMap{
"replaceNewline": func(s string) template.HTML {
return template.HTML(strings.Replace(template.HTMLEscapeString(s), "\n", "<br>", -1))
},
}).Parse(page))
t.Execute(os.Stdout, text)
}
Output
<!DOCTYPE html>
<html>
<body>
<p>first line<br><script>dangerous</script><br>last line</p>
</body>
</html>
Solution 5
It is unnecessary to pass your entire template as an unsafe template (and this is bad practice).
You should pass a map to your template, and only explicitly 'unsafe' the elements you want to use as such, eg.
package main
import "bytes"
import "fmt"
import "html/template"
import "strings"
var input = `
{{ define "LAYOUT" }}
<html>
<body>
{{ template "CONTENT" . }}
</body>
</html>
{{ end }}
{{ define "CONTENT" }}
Unsafe content: {{ .Unsafe }}
Newlines converted to <br/> follow:
{{ .Normal }}
{{ end }}
{{ template "LAYOUT" . }}
`
var other = `
Hello
World
Again
`
var other2 = `
<script>alert("Owned!");</script>
`
func main() {
var t, err = template.New("sample").Parse(input)
if err != nil {
panic(err)
}
var fixed = strings.Replace(other, "\n", "\n<br/>", -1)
var model = map[string]interface{}{
"Normal": template.HTML(fixed),
"Unsafe": other2,
}
var out bytes.Buffer
t.Execute(&out, model) # <--- !! Notice the model is NOT an HTML type.
var raw = out.String()
fmt.Printf("%s", raw)
}
Yields:
Unsafe content: <script>alert("Owned!");</script> Newlines converted to <br/> follow: <br/> Hello <br/> World <br/> Again <br/> </body> </html>
toasted_flakes
Updated on July 17, 2022Comments
-
toasted_flakes almost 2 years
I'm loading a text file that has newlines in it, and pass it to
html/templates
.Substituting the
\n
with<br>
in the loaded string, they are escaped by the template to html<br>
and displayed in the browser, instead of causing a line return.How can I change this behavior without switching to
text/templates
(which doesn't have XSS protection)? -
toasted_flakes over 11 yearsI can't cast it to
template.HTML
because my string isn't safe. Could you elaborate the pileline trick? Thanks a lot -
dskinner over 11 yearsif the string isn't safe then the pipeline isn't going to help either. Try splitting the string on
"\n"
and passing the resulting slice into the template. Userange
to print the string and insert<br>
. For example:Arr := strings.Split(myString, "\n")
in the go code, and{{range Arr}}{{.}}<br>{{end}}
in the template. -
JohnDoe over 11 yearsThis is actually the right track --- but saw, that the input is not save, which requires more processing, as correctly done in the accepted answer.
-
Nashenas over 10 years@dskinner, I know it's late, but you should write yours out as a full response. It's a much cleaner solution.
-
Keyur Padalia about 10 yearsWhile the idea is right, the implementation is wrong: marking a string as "safe" by wrapping it in
template.HTML(...)
disables all other escaping, so e.g. any single-line<script>
elements inother
would be passed through to the output verbatim. You need to add more escaping before markingfixed
as safe, e.g. by usingtemplate.HTMLEscape()
before substituting the newlines. -
IvanD about 3 yearsExcellent answer!