Skip to content

Commit

Permalink
Rework safe functions
Browse files Browse the repository at this point in the history
Closes #2252
Improves #2196
  • Loading branch information
jmooring committed May 26, 2024
1 parent e30d179 commit 6bca7bc
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 87 deletions.
14 changes: 14 additions & 0 deletions content/en/functions/_common/go-html-template-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
# Do not remove front matter.
---

Hugo uses Go's [text/template] and [html/template] packages.

The text/template package implements data-driven templates for generating textual output, while the html/template package implements data-driven templates for generating HTML output safe against code injection.

By default, Hugo uses the html/template package when rendering HTML files.

To generate HTML output that is safe against code injection, the html/template package escapes strings in certain contexts.

[text/template]: https://pkg.go.dev/text/template
[html/template]: https://pkg.go.dev/html/template
48 changes: 42 additions & 6 deletions content/en/functions/safe/CSS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: safe.CSS
description: Declares the given string as safe CSS string.
description: Declares the given string as a safe CSS string.
categories: []
keywords: []
action:
Expand All @@ -13,21 +13,57 @@ action:
- functions/safe/URL
returnType: template.CSS
signatures: [safe.CSS INPUT]
toc: true
aliases: [/functions/safecss]
---

In this context, *safe* means CSS content that matches any of the following:
## Introduction

{{% include "functions/_common/go-html-template-package.md" %}}

## Usage

Use the `safe.CSS` function to encapsulate known safe content that matches any of:

1. The CSS3 stylesheet production, such as `p { color: purple }`.
2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`.
3. CSS3 declaration productions, such as `color: red; margin: 2px`.
4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`.

Example: Given `style = "color: red;"` defined in the front matter of your `.md` file:
Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.

See the [Go documentation] for details.

[Go documentation]: https://pkg.go.dev/html/template#CSS

## Example

Without a safe declaration:

* `<p style="{{ .Params.style | safeCSS }}">…</p>` &rarr; `<p style="color: red;">…</p>`
* `<p style="{{ .Params.style }}">…</p>` &rarr; `<p style="ZgotmplZ">…</p>`
```go-html-template
{{ $style := "color: red;" }}
<p style="{{ $style }}">foo</p>
```

Hugo renders the above to:

```html
<p style="ZgotmplZ">foo</p>
```

{{% note %}}
`ZgotmplZ` is a special value that indicates that unsafe content reached a CSS or URL context.
`ZgotmplZ` is a special value that indicates that unsafe content reached a CSS or URL context at runtime.
{{% /note %}}

To declare the string as safe:

```go-html-template
{{ $style := "color: red;" }}
<p style="{{ $style | safeCSS }}">foo</p>
```

Hugo renders the above to:

```html
<p style="color: red;">foo</p>
```
43 changes: 32 additions & 11 deletions content/en/functions/safe/HTML.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,48 @@ action:
- functions/safe/URL
returnType: template.HTML
signatures: [safe.HTML INPUT]
toc: true
aliases: [/functions/safehtml]
---

It should not be used for HTML from a third-party, or HTML with unclosed tags or comments.
## Introduction

Given a site-wide [`hugo.toml`][config] with the following `copyright` value:
{{% include "functions/_common/go-html-template-package.md" %}}

{{< code-toggle file=hugo >}}
copyright = "© 2015 Jane Doe. <a href=\"https://creativecommons.org/licenses/by/4.0/\">Some rights reserved</a>."
{{< /code-toggle >}}
## Usage

`{{ .Site.Copyright | safeHTML }}` in a template would then output:
Use the `safe.HTML` function to encapsulate a known safe HTML document fragment. It should not be used for HTML from a third-party, or HTML with unclosed tags or comments.

```html
© 2015 Jane Doe. <a href="https://creativecommons.org/licenses/by/4.0/">Some rights reserved</a>.
Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.

See the [Go documentation] for details.

[Go documentation]: https://pkg.go.dev/html/template#HTML

## Example

Without a safe declaration:

```go-html-template
{{ $html := "<em>emphasized</em>" }}
{{ $html }}
```

However, without the `safeHTML` function, html/template assumes `.Site.Copyright` to be unsafe and therefore escapes all HTML tags and renders the whole string as plain text:
Hugo renders the above to:

```html
<p>© 2015 Jane Doe. &lt;a href=&#34;https://creativecommons.org/licenses by/4.0/&#34;&gt;Some rights reserved&lt;/a&gt;.</p>
&lt;em&gt;emphasized&lt;/em&gt;
```

[config]: /getting-started/configuration/
To declare the string as safe:

```go-html-template
{{ $html := "<em>emphasized</em>" }}
{{ $html | safeHTML }}
```

Hugo renders the above to:

```html
<em>emphasized</em>
```
51 changes: 31 additions & 20 deletions content/en/functions/safe/HTMLAttr.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,54 @@ action:
- functions/safe/URL
returnType: template.HTMLAttr
signatures: [safe.HTMLAttr INPUT]
toc: true
aliases: [/functions/safehtmlattr]
---

Given a site configuration that contains this menu entry:
## Introduction

{{< code-toggle file=hugo >}}
[[menus.main]]
name = "IRC"
url = "irc://irc.freenode.net/#golang"
{{< /code-toggle >}}
{{% include "functions/_common/go-html-template-package.md" %}}

Attempting to use the `url` value directly in an attribute:
## Usage

Use the `safe.HTMLAttr` function to encapsulate an HTML attribute from a trusted source.

Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.

See the [Go documentation] for details.

[Go documentation]: https://pkg.go.dev/html/template#HTMLAttr

## Example

Without a safe declaration:

```go-html-template
{{ range site.Menus.main }}
<a href="{{ .URL }}">{{ .Name }}</a>
{{ with .Date }}
{{ $humanDate := time.Format "2 Jan 2006" . }}
{{ $machineDate := time.Format "2006-01-02T15:04:05-07:00" . }}
<time datetime="{{ $machineDate }}">{{ $humanDate }}</time>
{{ end }}
```

Will produce:
Hugo renders the above to:

```html
<a href="#ZgotmplZ">IRC</a>
<time datetime="2024-05-26T07:19:55&#43;02:00">26 May 2024</time>
```

`ZgotmplZ` is a special value, inserted by Go's [template/html] package, that indicates that unsafe content reached a CSS or URL context.

To indicate that the HTML attribute is safe:
To declare the key-value pair as safe:

```go-html-template
{{ range site.Menus.main }}
<a {{ printf "href=%q" .URL | safeHTMLAttr }}>{{ .Name }}</a>
{{ with .Date }}
{{ $humanDate := time.Format "2 Jan 2006" . }}
{{ $machineDate := time.Format "2006-01-02T15:04:05-07:00" . }}
<time {{ printf "datetime=%q" $machineDate | safeHTMLAttr }}>{{ $humanDate }}</time>
{{ end }}
```

{{% note %}}
As demonstrated above, you must pass the HTML attribute name _and_ value through the function. Applying `safeHTMLAttr` to the attribute value has no effect.
{{% /note %}}
Hugo renders the above to:

[template/html]: https://pkg.go.dev/html/template
```html
<time datetime="2024-05-26T07:19:55+02:00">26 May 2024</time>
```
50 changes: 45 additions & 5 deletions content/en/functions/safe/JS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,54 @@ action:
- functions/safe/URL
returnType: template.JS
signatures: [safe.JS INPUT]
toc: true
aliases: [/functions/safejs]
---

In this context, *safe* means the string encapsulates a known safe EcmaScript5 Expression (e.g., `(x + y * z())`).
## Introduction

Template authors are responsible for ensuring that typed expressions do not break the intended precedence and that there is no statement/expression ambiguity as when passing an expression like `{ foo:bar() }\n['foo']()`, which is both a valid expression and a valid program with a very different meaning.
{{% include "functions/_common/go-html-template-package.md" %}}

Example: Given `hash = "619c16f"` defined in the front matter of your `.md` file:
## Usage

* `<script>var form_{{ .Params.hash | safeJS }};…</script>` &rarr; `<script>var form_619c16f;…</script>`
* `<script>var form_{{ .Params.hash }};…</script>` &rarr; `<script>var form_"619c16f";…</script>`
Use the `safe.JS` function to encapsulate a known safe EcmaScript5 Expression.

Template authors are responsible for ensuring that typed expressions do not break the intended precedence and that there is no statement/expression ambiguity as when passing an expression like `{ foo: bar() }\n['foo']()`, which is both a valid Expression and a valid Program with a very different meaning.

Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.

Using the `safe.JS` function to include valid but untrusted JSON is not safe. A safe alternative is to parse the JSON with the [`transform.Unmarshal`] function and then pass the resultant object into the template, where it will be converted to sanitized JSON when presented in a JavaScript context.

[`transform.Unmarshal`]: /functions/transform/unmarshal/

See the [Go documentation] for details.

[Go documentation]: https://pkg.go.dev/html/template#JS

## Example

Without a safe declaration:

```go-html-template
{{ $js := "x + y" }}
<script>const a = {{ $js }}</script>
```

Hugo renders the above to:

```html
<script>const a = "x + y"</script>
```

To declare the string as safe:

```go-html-template
{{ $js := "x + y" }}
<script>const a = {{ $js | safeJS }}</script>
```

Hugo renders the above to:

```html
<script>const a = x + y</script>
```
29 changes: 21 additions & 8 deletions content/en/functions/safe/JSStr.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,27 @@ action:
- functions/safe/URL
returnType: template.JSStr
signatures: [safe.JSStr INPUT]
toc: true
aliases: [/functions/safejsstr]
---

Encapsulates a sequence of characters meant to be embedded between quotes in a JavaScript expression. Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.

Without declaring a variable to be a safe JavaScript string:
## Introduction

{{% include "functions/_common/go-html-template-package.md" %}}

## Usage

Use the `safe.JSStr` function to encapsulate a sequence of characters meant to be embedded between quotes in a JavaScript expression.

Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.

See the [Go documentation] for details.

[Go documentation]: https://pkg.go.dev/html/template#JSStr

## Example

Without a safe declaration:

```go-html-template
{{ $title := "Lilo & Stitch" }}
Expand All @@ -27,15 +42,15 @@ Without declaring a variable to be a safe JavaScript string:
</script>
```

Rendered:
Hugo renders the above to:

```html
<script>
const a = "Title: " + "Lilo \u0026 Stitch";
</script>
```

To avoid escaping by Go's [html/template] package:
To declare the string as safe:

```go-html-template
{{ $title := "Lilo & Stitch" }}
Expand All @@ -44,12 +59,10 @@ To avoid escaping by Go's [html/template] package:
</script>
```

Rendered:
Hugo renders the above to:

```html
<script>
const a = "Title: " + "Lilo & Stitch";
</script>
```

[html/template]: https://pkg.go.dev/html/template
Loading

0 comments on commit 6bca7bc

Please sign in to comment.