From: Mike Samuel Date: Wed, 14 Sep 2011 21:21:20 +0000 (-0700) Subject: exp/template/html: flesh out package documentation. X-Git-Tag: weekly.2011-09-16~25 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=23fab11c47804b449f8498d8a21e54cb966fd989;p=gostls13.git exp/template/html: flesh out package documentation. R=nigeltao, r CC=golang-dev https://golang.org/cl/4969078 --- diff --git a/src/pkg/exp/template/html/Makefile b/src/pkg/exp/template/html/Makefile index 620404b5ef..0398c78fd6 100644 --- a/src/pkg/exp/template/html/Makefile +++ b/src/pkg/exp/template/html/Makefile @@ -9,6 +9,7 @@ GOFILES=\ clone.go\ context.go\ css.go\ + doc.go\ escape.go\ html.go\ js.go\ diff --git a/src/pkg/exp/template/html/doc.go b/src/pkg/exp/template/html/doc.go new file mode 100644 index 0000000000..12a3b1e580 --- /dev/null +++ b/src/pkg/exp/template/html/doc.go @@ -0,0 +1,394 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package html is a specialization of package template that automates the +construction of HTML output that is safe against code injection. + + +Introduction + +To use this package, invoke the standard template package to parse a template +set, and then use this package’s EscapeSet function to secure the set. +The arguments to EscapeSet are the template set and the names of all templates +that will be passed to Execute. + + set, err := new(template.Set).Parse(...) + set, err = EscapeSet(set, "templateName0", ...) + +If successful, set will now be injection-safe. Otherwise, the returned set will +be nil and an error, described below, will explain the problem. +If an error is returned, do not use the original set; it is insecure. + +The template names do not need to include helper templates but should include +all names x used thus: + + set.Execute(out, x, ...) + +EscapeSet modifies the named templates in place to treat data values as plain +text safe for embedding in an HTML document. The escaping is contextual, so +actions can appear within JavaScript, CSS, and URI contexts without introducing'hazards. + +The security model used by this package assumes that template authors are +trusted, while Execute's data parameter is not. More details are provided below. + +Example + + tmpls, err := new(template.Set).Parse(`{{define "t'}}Hello, {{.}}!{{end}}`) + +when used by itself + + tmpls.Execute(out, "t", "") + +produces + + Hello, ! + +but after securing with EscapeSet like this, + + tmpls, err := EscapeSet(tmpls, "t") + tmpls.Execute(out, "t", ...) + +produces the safe, escaped HTML output + + Hello, <script>alert('you have been pwned')</script>! + + +Contexts + +EscapeSet understands HTML, CSS, JavaScript, and URIs. It adds sanitizing +functions to each simple action pipeline, so given the excerpt + + {{.}} + +EscapeSet will rewrite each {{.}} to add escaping functions where necessary, +in this case, + + {{. | html}} + + +Errors + +This section describes the errors returned by EscapeSet. Each error is +illustrated by an example that triggers the error, followed by an explanation +of the problem. + +Error: "... appears in an ambiguous URL context" +Example: + +Discussion: + {{.X}} is in an ambiguous URL context since, depending on {{.C}}, it may be + either a URL suffix or a query parameter. + Moving {{.X}} into the condition removes the ambiguity: + + + +Error: "... appears inside a comment" +Example: +*/ +// +// +// +/* +Discussion: + {{.X}} appears inside a comment. There is no escaping convention for + comments. To use IE conditional comments, inject the + whole comment as a type string (see below). + To comment out code, break the {{...}}. + +Error: "{{if}} branches end in different contexts" +Example: + {{if .C}}{{end}} <- No quote after foo + + Second, try refactoring your template. + + {{if .C}}{{end}} + + -> + + {{if .C}}{{else}}{{.X}}{{end}} + + Third, check for {{range}}s that have no {{else}} + + + + looks good, but if {{.}} is empty then the URL is /search&x=... + where {{.X}} is not guaranteed to be in a URL query. + EscapeSet cannot prove which {{range}} collections are never non-empty, so + add an {{else}} + + + + -> + + + + Fourth, contact the mailing list. You may have a useful pattern that + EscapeSet does not yet support, and we can work with you. + + +Error: "... ends in a non-text context: ..." +Examples: +
{{template "helper"}} {{end}} + {{define "helper"}} document.write('
{{end}} + {{define "attrs"}}href="{{.URL}}"{{end}} +Discussion: + EscapeSet looks through template calls to compute the context. + Here the {{.URL}} in "attrs" must be treated as a URL when called from "main", + but if "attrs" is not in set when EscapeSet(&set, "main") is called, this + error will arise. + +Error: "on range loop re-entry: ..." +Example: + {{range .}}

tag is missing a '>'. + EscapeSet cannot tell whether {{.}} is meant to be an HTML class or the + content of a broken

element and complains because the second iteration + would produce something like + +

alert("\{{.X}}") +Discussion: + EscapeSet does not support actions following a backslash. + This is usually an error and there are better solutions; for + our example + + should work, and if {{.X}} is a partial escape sequence such as + "xA0", give it the type ContentTypeJSStr and include the whole + sequence, as in + {`\xA0`, ContentTypeJSStr} + +Error: "unfinished JS regexp charset in ..." +Example: + +Discussion: + EscapeSet does not support interpolation into regular expression literal + character sets. + +Error: "ZgotmplZ" +Example: + + where {{.X}} evaluates to `javascript:...` +Discussion: + "ZgotmplZ" is a special value that indicates that unsafe content reached + a CSS or URL context at runtime. The output of the example will be + + If the data can be trusted, giving the string type XXX will exempt + it from filtering. + +A fuller picture + +The rest of this package comment may be skipped on first reading; it includes +details necessary to understand escaping contexts and error messages. Most users +will not need to understand these details. + + + + +Contexts + +Assuming {{.}} is `O'Reilly: How are you?`, the table below shows +how {{.}} appears when used in the context to the left. + +Context {{.}} After +{{.}} O'Reilly: How are <i>you</i>? + O'Reilly: How are you? + O'Reilly: How are %3ci%3eyou%3c/i%3e? + O'Reilly%3a%20How%20are%3ci%3e...%3f + O\x27Reilly: How are \x3ci\x3eyou...? + "O\x27Reilly: How are \x3ci\x3eyou...?" + O\x27Reilly: How are \x3ci\x3eyou...\x3f + +If used in an unsafe context, then the value might be filtered out: + +Context {{.}} After + #ZgotmplZ + +since "O'Reilly:" is not an allowed protocol like "http:". + + +If {{.}} is the innocuous word, `left`, then it can appear more widely, + +Context {{.}} After +{{.}} left + left + left + left + left + left + left + left + left + +Non-string values can be used in JavaScript contexts. +If {{.}} is + + []struct{A,B string}{ "foo", "bar" } + +in the escaped template + + + +then the template output is + + + +See package json to understand how non-string content is marshalled for +embedding in JavaScript contexts. + + +Typed Strings + +By default, EscapeSet assumes all pipelines produce a plain text string. It +adds escaping pipeline stages necessary to correctly and safely embed that +plain text string in the appropriate context. + +When a data value is not plain text, you can make sure it is not over-escaped +by marking it with its type. + +A value that implements interface TypedStringer can carry known-safe content. + + type safeHTML struct{} + func (s safeHTML) String() string { return `World` } + func (s safeHTML) ContentType() ContentType { return ContentTypeHTML } + +The template + + Hello, {{.}}! + +can be invoked with + + tmpl.Execute(out, safeHTML{}) + +to produce + + Hello, World! + +instead of the + + Hello, <b>World<b>! + +which would have been produced if {{.}} did not implement TypedStringer. + +ContentTypeHTML attaches to a well-formed HTML DocumentFragment. +Do not use it for HTML from a third-party, or HTML with unclosed tags or +comments. The outputs of a sound HTML sanitizer and a template escaped by +this package are examples of ContentTypeHTML. + +ContentTypeCSS attaches to a well-formed safe content that matches: +(1) The CSS3 stylesheet production, for example `p { color: purple }` +(2) The CSS3 rule production, for example `a[href=~"https:"].foo#bar` +(3) CSS3 declaration productions, for example `color: red; margin: 2px` +(4) The CSS3 value production, for example `rgba(0, 0, 255, 127)` + +ContentTypeJS attaches to a well-formed JavaScript (EcmaScript5) Expression +production, for example `(x + y * z())`. 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. + +ContentTypeJSStr attaches to a snippet of \-escaped characters that could be +quoted to form a JavaScript string literal. For example, foo\nbar with quotes +around it makes a valid JavaScript string literal. + +ContentTypeURL attaches to a URL fragment from a trusted source. +A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()` +from a trusted source should go in the page, but by default dynamic +`javascript:` URLs are filtered out since they are a frequently +successfully exploited injection vector. + + +Security Model + +http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package. + +This package assumes that template authors are trusted, that Execute's data +parameter is not, and seeks to preserve the properties below in the face +of untrusted data: + +Structure Preservation Property +"... when a template author writes an HTML tag in a safe templating language, +the browser will interpret the corresponding portion of the output as a tag +regardless of the values of untrusted data, and similarly for other structures +such as attribute boundaries and JS and CSS string boundaries." + +Code Effect Property +"... only code specified by the template author should run as a result of +injecting the template output into a page and all code specified by the +template author should run as a result of the same." + +Least Surprise Property +"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript; +who knows that EscapeSet is applied should be able to look at a {{.}} +and correctly infer what sanitization happens." +*/ +package html diff --git a/src/pkg/exp/template/html/escape.go b/src/pkg/exp/template/html/escape.go index 3c0996c46a..a1816fc71c 100644 --- a/src/pkg/exp/template/html/escape.go +++ b/src/pkg/exp/template/html/escape.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package html is a specialization of template that automates the -// construction of safe HTML output. -// INCOMPLETE. package html import (