mirror of
https://github.com/golang/go
synced 2024-11-12 05:50:21 -07:00
exp/template/html: allow commenting out of actions
Instead of erroring on actions inside comments, use existing escaping pipeline to quash the output of actions inside comments. If a template maintainer uses a comment to disable template code: {{if .}}Hello, {{.}}!{{end}} -> <!--{{if true}}Hello, {{.}}!{{end}}--> will result in <!--Hello, !--> regardless of the value of {{.}}. In a later CL, comment elision will result in the entire commented-out section being dropped from the template output. Any side-effects in pipelines, such as panics, will still be realized. R=nigeltao CC=golang-dev https://golang.org/cl/5078041
This commit is contained in:
parent
533b372280
commit
8bc5ef6cd7
@ -100,19 +100,6 @@ const (
|
||||
// produce a valid JavaScript Program.
|
||||
ErrEndContext
|
||||
|
||||
// ErrInsideComment: "... appears inside a comment"
|
||||
// Example:
|
||||
// <!-- {{.X}} -->
|
||||
// <script>/* {{.X}} */</script>
|
||||
// <style>/* {{.X}} */</style>
|
||||
//
|
||||
// Discussion:
|
||||
// {{.X}} appears inside a comment. There is no escaping convention for
|
||||
// comments. To use IE conditional comments, inject the whole comment
|
||||
// as an HTML, JS, or CSS value (see content.go).
|
||||
// To comment out code, break the {{...}}.
|
||||
ErrInsideComment
|
||||
|
||||
// ErrNoNames: "must specify names of top level templates"
|
||||
//
|
||||
// EscapeSet does not assume that all templates in a set produce HTML.
|
||||
|
@ -64,6 +64,7 @@ func EscapeSet(s *template.Set, names ...string) (*template.Set, os.Error) {
|
||||
// funcMap maps command names to functions that render their inputs safe.
|
||||
var funcMap = template.FuncMap{
|
||||
"exp_template_html_attrescaper": attrEscaper,
|
||||
"exp_template_html_commentescaper": commentEscaper,
|
||||
"exp_template_html_cssescaper": cssEscaper,
|
||||
"exp_template_html_cssvaluefilter": cssValueFilter,
|
||||
"exp_template_html_htmlnamefilter": htmlNameFilter,
|
||||
@ -200,12 +201,10 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
|
||||
s = append(s, "exp_template_html_htmlnamefilter")
|
||||
default:
|
||||
if isComment(c.state) {
|
||||
return context{
|
||||
state: stateError,
|
||||
err: errorf(ErrInsideComment, n.Line, "%s appears inside a comment", n),
|
||||
}
|
||||
s = append(s, "exp_template_html_commentescaper")
|
||||
} else {
|
||||
panic("unexpected state " + c.state.String())
|
||||
}
|
||||
panic("unexpected state " + c.state.String())
|
||||
}
|
||||
switch c.delim {
|
||||
case delimNone:
|
||||
|
@ -361,11 +361,94 @@ func TestEscape(t *testing.T) {
|
||||
`<a style="border-image: url(/**/%27%22;://%20%5c), url("/**/%27%22;://%20%5c"), url('/**/%27%22;://%20%5c'), 'http://www.example.com/?q=%2f%2a%2a%2f%27%22%3b%3a%2f%2f%20%5c''">`,
|
||||
},
|
||||
{
|
||||
"comment",
|
||||
"HTML comment",
|
||||
"<b>Hello, <!-- name of world -->{{.C}}</b>",
|
||||
// TODO: Elide comment.
|
||||
"<b>Hello, <!-- name of world --><Cincinatti></b>",
|
||||
},
|
||||
{
|
||||
"Split HTML comment",
|
||||
"<b>Hello, <!-- name of {{if .T}}city -->{{.C}}{{else}}world -->{{.W}}{{end}}</b>",
|
||||
"<b>Hello, <!-- name of city --><Cincinatti></b>",
|
||||
},
|
||||
{
|
||||
"JS line comment",
|
||||
"<script>for (;;) { if (c()) break// foo not a label\n" +
|
||||
"foo({{.T}});}</script>",
|
||||
"<script>for (;;) { if (c()) break// foo not a label\n" +
|
||||
"foo( true );}</script>",
|
||||
},
|
||||
{
|
||||
"JS multiline block comment",
|
||||
"<script>for (;;) { if (c()) break/* foo not a label\n" +
|
||||
" */foo({{.T}});}</script>",
|
||||
// Newline separates break from call. If newline
|
||||
// removed, then break will consume label leaving
|
||||
// code invalid.
|
||||
"<script>for (;;) { if (c()) break/* foo not a label\n" +
|
||||
" */foo( true );}</script>",
|
||||
},
|
||||
{
|
||||
"JS single-line block comment",
|
||||
"<script>for (;;) {\n" +
|
||||
"if (c()) break/* foo a label */foo;" +
|
||||
"x({{.T}});}</script>",
|
||||
// Newline separates break from call. If newline
|
||||
// removed, then break will consume label leaving
|
||||
// code invalid.
|
||||
"<script>for (;;) {\n" +
|
||||
"if (c()) break/* foo a label */foo;" +
|
||||
"x( true );}</script>",
|
||||
},
|
||||
{
|
||||
"JS block comment flush with mathematical division",
|
||||
"<script>var a/*b*//c\nd</script>",
|
||||
"<script>var a/*b*//c\nd</script>",
|
||||
},
|
||||
{
|
||||
"JS mixed comments",
|
||||
"<script>var a/*b*///c\nd</script>",
|
||||
"<script>var a/*b*///c\nd</script>",
|
||||
},
|
||||
{
|
||||
"CSS comments",
|
||||
"<style>p// paragraph\n" +
|
||||
`{border: 1px/* color */{{"#00f"}}}</style>`,
|
||||
"<style>p// paragraph\n" +
|
||||
"{border: 1px/* color */#00f}</style>",
|
||||
},
|
||||
{
|
||||
"JS attr block comment",
|
||||
`<a onclick="f(""); /* alert({{.H}}) */">`,
|
||||
// Attribute comment tests should pass if the comments
|
||||
// are successfully elided.
|
||||
`<a onclick="f(""); /* alert() */">`,
|
||||
},
|
||||
{
|
||||
"JS attr line comment",
|
||||
`<a onclick="// alert({{.G}})">`,
|
||||
`<a onclick="// alert()">`,
|
||||
},
|
||||
{
|
||||
"CSS attr block comment",
|
||||
`<a style="/* color: {{.H}} */">`,
|
||||
`<a style="/* color: */">`,
|
||||
},
|
||||
{
|
||||
"CSS attr line comment",
|
||||
`<a style="// color: {{.G}}">`,
|
||||
`<a style="// color: ">`,
|
||||
},
|
||||
{
|
||||
"HTML substitution commented out",
|
||||
"<p><!-- {{.H}} --></p>",
|
||||
"<p><!-- --></p>",
|
||||
},
|
||||
{
|
||||
"Comment ends flush with start",
|
||||
"<!--{{.}}--><script>/*{{.}}*///{{.}}\n</script><style>/*{{.}}*///{{.}}\n</style><a onclick='/*{{.}}*///{{.}}' style='/*{{.}}*///{{.}}'>",
|
||||
"<!----><script>/**///\n</script><style>/**///\n</style><a onclick='/**///' style='/**///'>",
|
||||
},
|
||||
{
|
||||
"typed HTML in text",
|
||||
`{{.W}}`,
|
||||
@ -717,26 +800,6 @@ func TestErrors(t *testing.T) {
|
||||
`<a onclick="/foo[\]/`,
|
||||
`unfinished JS regexp charset: "foo[\\]/"`,
|
||||
},
|
||||
{
|
||||
`<a onclick="/* alert({{.X}}) */">`,
|
||||
`z:1: (action: [(command: [F=[X]])]) appears inside a comment`,
|
||||
},
|
||||
{
|
||||
`<a onclick="// alert({{.X}})">`,
|
||||
`z:1: (action: [(command: [F=[X]])]) appears inside a comment`,
|
||||
},
|
||||
{
|
||||
`<a style="/* color: {{.X}} */">`,
|
||||
`z:1: (action: [(command: [F=[X]])]) appears inside a comment`,
|
||||
},
|
||||
{
|
||||
`<a style="// color: {{.X}}">`,
|
||||
`z:1: (action: [(command: [F=[X]])]) appears inside a comment`,
|
||||
},
|
||||
{
|
||||
"<!-- {{.H}} -->",
|
||||
"z:1: (action: [(command: [F=[H]])]) appears inside a comment",
|
||||
},
|
||||
{
|
||||
// It is ambiguous whether 1.5 should be 1\.5 or 1.5.
|
||||
// Either `var x = 1/- 1.5 /i.test(x)`
|
||||
|
@ -224,3 +224,13 @@ func htmlNameFilter(args ...interface{}) string {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// commentEscaper returns the empty string regardless of input.
|
||||
// Comment content does not correspond to any parsed structure or
|
||||
// human-readable content, so the simplest and most secure policy is to drop
|
||||
// content interpolated into comments.
|
||||
// This approach is equally valid whether or not static comment content is
|
||||
// removed from the template.
|
||||
func commentEscaper(args ...interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user