Plus (+) is escaped despite safeHTML

I’m working on my template’s head.html partial, trying to add RSS links.

{{- range .AlternativeOutputFormats }}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .RelPermalink }}" title="title" />
{{- end }}

But the output I’m getting is:

<link rel="alternate" type="application/rss&#43;xml" href="/index.xml" title="title" /> 

Note the &#43;, that should be a plus sign.

I checked that .MediaType.Type has an actual plus (and not the escaped sequence) and I tried it with | safeHTML and safeHTMLAttr and safeURL but nothing seems to work.

The only thing that fixes it is the advise given here which is to add isPlainText = true to outputFormats.html however that does not work for me as I can’t add it to my theme’s theme.toml file. I’m also thinking that that it might not be a good idea to set that on HTML especially in a theme, I’m thinking it should be possible to output a ‘+’ there with isPlainText = false.

This seems like a bug to me, or am I wrong? I’m including the head.html partial directly from baseof.html like this: {{ partial "site/head" . }} I tried this on Hugo .70 and .92, both have this issue.

Any advise?

<link rel="{{ .Rel }}" {{ printf "type=%q" .MediaType.Type | safeHTMLAttr }} href="{{ .RelPermalink }}" title="title" />

OR

<link rel="{{ .Rel }}" {{ .MediaType.Type | printf "type=%q" | safeHTMLAttr }} href="{{ .RelPermalink }}" title="title" />

I think I like the second one better.

2 Likes

Thanks, @jmooring that fixes it.

I’m curious though, should something marked with safeHTML not be considered “do not touch”? I noticed it even needs to have the “type=” in there to work. Why?

This is part of Go’s html/template security model. If you really want the details:
https://pkg.go.dev/html/template#hdr-Security_Model

Oh, so does isPlainText = true just make Hugo use the text/template instead? Because then I think isPlainText = true should be the default for HTML. Pointless security is pointless.

Is there a way to set isPlainText = true in theme.toml? I tried the obvious approach, it did not work.

Ok, so I just realized that you can (should?) have both a theme and a site config in your theme directory. So the output format settings work if I place them in the config.toml in my theme folder.

So I set isPlainText = true in [outputFormats.html]. I think this is safe. From html/template:

The security model used by this package assumes that template authors are trusted, while Execute’s data parameter is not.

This is true for it’s intended purpose of a server-side templating module with parameters coming from an HTTP request, but this is not true for Hugo where you generate static files offline. In Hugo the data parameters come from either Hugo itself or from the content markdown files. Both are trusted and there is no point in placing security at that level. An attacker would have to gain access to your theme or your site content source at which point they could use safeHTML or the workaround above and besides at that point you have bigger problems. I even think it is less then pointless as it causes unnecessary inconvenience for the template and site authors. And for site authors / editors there is another layer of “security” in the markdown parser.

So by setting isPlainText = true by default on everything you lose a whole layer of issues you have to work around with no loss in security or functionality and probably also increase performance. I actually see no point in even having that setting or using html/template (instead of text/template) anywhere in Hugo.

Please correct me if I’m wrong. No, seriously. This seems to be an integral part of Hugo and I’m surprised by the way it is working now. I’m kind of new to Hugo (but not to SW development or SW security) so I might be missing something.

This statement may be true for your project, but it is not true for others. Every few months someone brings this up, and the conversation always ends the same way: “I didn’t think about that.”

Given the workaround you posted above, I can’t imagine how you could deal with untrusted content in your theme or markdown. Setting isPlainText = false does not solve any security problem, you need much more then that.

If you need to allow public access to your markdown content, Hugo is simply the wrong tool and IMHO trying to cater to that audience is futile. There is plenty of other solutions around for that.

The fact that “every few months” this comes up should make you think about that. That said, It’s your decision and given your answer and what I know now I’m perfectly fine with setting isPlainText = true and not think about it any more. I appreciate your answer, and the Hugo team’s efforts. Maybe a clarification in the docs on what isPlainText actually does and why you would want to use it would be helpful.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.