html/templates - Replacing newlines with <br>

10,029

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>&lt;script&gt;dangerous&lt;/script&gt;<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>"))
}

Try it out!

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>&lt;script&gt;dangerous&lt;/script&gt;<br>last line</p>
  </body>
</html>

Go Playground

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:    &lt;script&gt;alert(&#34;Owned!&#34;);&lt;/script&gt;

Newlines converted to <br/> follow:
 <br/>  Hello 
 <br/>  World 
 <br/>  Again 
 <br/>

  </body>
</html>
Share:
10,029
toasted_flakes
Author by

toasted_flakes

Updated on July 17, 2022

Comments

  • toasted_flakes
    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 &lt;br&gt; 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
    toasted_flakes over 11 years
    I can't cast it to template.HTML because my string isn't safe. Could you elaborate the pileline trick? Thanks a lot
  • dskinner
    dskinner over 11 years
    if 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. Use range 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
    JohnDoe over 11 years
    This 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
    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
    Keyur Padalia about 10 years
    While 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 in other would be passed through to the output verbatim. You need to add more escaping before marking fixed as safe, e.g. by using template.HTMLEscape() before substituting the newlines.
  • IvanD
    IvanD about 3 years
    Excellent answer!