Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
G
golang
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
golang
Commits
33705dde
Commit
33705dde
authored
Jul 06, 2011
by
David Symonds
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
exp/template: add a JavaScript escaper.
R=r CC=golang-dev
https://golang.org/cl/4671048
parent
a33cc423
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
125 additions
and
13 deletions
+125
-13
exec_test.go
src/pkg/exp/template/exec_test.go
+20
-0
funcs.go
src/pkg/exp/template/funcs.go
+105
-13
No files found.
src/pkg/exp/template/exec_test.go
View file @
33705dde
...
...
@@ -158,6 +158,8 @@ var execTests = []execTest{
"<script>alert("XSS");</script>"
,
nil
,
true
},
{
"html pipeline"
,
`{{printf "<script>alert(\"XSS\");</script>" | html}}`
,
"<script>alert("XSS");</script>"
,
nil
,
true
},
// JS.
{
"js"
,
`{{js .}}`
,
`It\'d be nice.`
,
`It'd be nice.`
,
true
},
// Booleans
{
"not"
,
"{{not true}} {{not false}}"
,
"false true"
,
nil
,
true
},
{
"and"
,
"{{and 0 0}} {{and 1 0}} {{and 0 1}} {{and 1 1}}"
,
"false false false true"
,
nil
,
true
},
...
...
@@ -248,3 +250,21 @@ func TestExecuteError(t *testing.T) {
t
.
Errorf
(
"expected os.EPERM; got %s"
,
err
)
}
}
func
TestJSEscaping
(
t
*
testing
.
T
)
{
testCases
:=
[]
struct
{
in
,
exp
string
}{
{
`a`
,
`a`
},
{
`'foo`
,
`\'foo`
},
{
`Go "jump" \`
,
`Go \"jump\" \\`
},
{
`Yukihiro says "今日は世界"`
,
`Yukihiro says \"今日は世界\"`
},
{
"unprintable
\uFDFF
"
,
`unprintable \uFDFF`
},
}
for
_
,
tc
:=
range
testCases
{
s
:=
JSEscapeString
(
tc
.
in
)
if
s
!=
tc
.
exp
{
t
.
Errorf
(
"JS escaping [%s] got [%s] want [%s]"
,
tc
.
in
,
s
,
tc
.
exp
)
}
}
}
src/pkg/exp/template/funcs.go
View file @
33705dde
...
...
@@ -6,10 +6,12 @@ package template
import
(
"bytes"
"io"
"fmt"
"io"
"reflect"
"strings"
"unicode"
"utf8"
)
// FuncMap is the type of the map defining the mapping from names to functions.
...
...
@@ -20,6 +22,7 @@ type FuncMap map[string]interface{}
var
funcs
=
map
[
string
]
reflect
.
Value
{
"printf"
:
reflect
.
ValueOf
(
fmt
.
Sprintf
),
"html"
:
reflect
.
ValueOf
(
HTMLEscaper
),
"js"
:
reflect
.
ValueOf
(
JSEscaper
),
"and"
:
reflect
.
ValueOf
(
and
),
"or"
:
reflect
.
ValueOf
(
or
),
"not"
:
reflect
.
ValueOf
(
not
),
...
...
@@ -98,34 +101,34 @@ func not(arg interface{}) (truth bool) {
// HTML escaping.
var
(
esc
Quot
=
[]
byte
(
"""
)
// shorter than """
esc
Apos
=
[]
byte
(
"'"
)
// shorter than "'"
esc
Amp
=
[]
byte
(
"&"
)
esc
Lt
=
[]
byte
(
"<"
)
esc
Gt
=
[]
byte
(
">"
)
html
Quot
=
[]
byte
(
"""
)
// shorter than """
html
Apos
=
[]
byte
(
"'"
)
// shorter than "'"
html
Amp
=
[]
byte
(
"&"
)
html
Lt
=
[]
byte
(
"<"
)
html
Gt
=
[]
byte
(
">"
)
)
// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
func
HTMLEscape
(
w
io
.
Writer
,
b
[]
byte
)
{
last
:=
0
for
i
,
c
:=
range
b
{
var
esc
[]
byte
var
html
[]
byte
switch
c
{
case
'"'
:
esc
=
esc
Quot
html
=
html
Quot
case
'\'
'
:
esc
=
esc
Apos
html
=
html
Apos
case
'&'
:
esc
=
esc
Amp
html
=
html
Amp
case
'<'
:
esc
=
esc
Lt
html
=
html
Lt
case
'>'
:
esc
=
esc
Gt
html
=
html
Gt
default
:
continue
}
w
.
Write
(
b
[
last
:
i
])
w
.
Write
(
esc
)
w
.
Write
(
html
)
last
=
i
+
1
}
w
.
Write
(
b
[
last
:
])
...
...
@@ -155,3 +158,92 @@ func HTMLEscaper(args ...interface{}) string {
}
return
HTMLEscapeString
(
s
)
}
// JavaScript escaping.
var
(
jsLowUni
=
[]
byte
(
`\u00`
)
hex
=
[]
byte
(
"0123456789ABCDEF"
)
jsBackslash
=
[]
byte
(
`\\`
)
jsApos
=
[]
byte
(
`\'`
)
jsQuot
=
[]
byte
(
`\"`
)
)
// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
func
JSEscape
(
w
io
.
Writer
,
b
[]
byte
)
{
last
:=
0
for
i
:=
0
;
i
<
len
(
b
);
i
++
{
c
:=
b
[
i
]
if
' '
<=
c
&&
c
<
utf8
.
RuneSelf
&&
c
!=
'\\'
&&
c
!=
'"'
&&
c
!=
'\'
'
{
// fast path: nothing to do
continue
}
w
.
Write
(
b
[
last
:
i
])
if
c
<
utf8
.
RuneSelf
{
// Quotes and slashes get quoted.
// Control characters get written as \u00XX.
switch
c
{
case
'\\'
:
w
.
Write
(
jsBackslash
)
case
'\'
'
:
w
.
Write
(
jsApos
)
case
'"'
:
w
.
Write
(
jsQuot
)
default
:
w
.
Write
(
jsLowUni
)
t
,
b
:=
c
>>
4
,
c
&
0x0f
w
.
Write
(
hex
[
t
:
t
+
1
])
w
.
Write
(
hex
[
b
:
b
+
1
])
}
}
else
{
// Unicode rune.
rune
,
size
:=
utf8
.
DecodeRune
(
b
[
i
:
])
if
unicode
.
IsPrint
(
rune
)
{
w
.
Write
(
b
[
i
:
i
+
size
])
}
else
{
// TODO(dsymonds): Do this without fmt?
fmt
.
Fprintf
(
w
,
"
\\
u%04X"
,
rune
)
}
i
+=
size
-
1
}
last
=
i
+
1
}
w
.
Write
(
b
[
last
:
])
}
// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
func
JSEscapeString
(
s
string
)
string
{
// Avoid allocation if we can.
if
strings
.
IndexFunc
(
s
,
jsIsSpecial
)
<
0
{
return
s
}
var
b
bytes
.
Buffer
JSEscape
(
&
b
,
[]
byte
(
s
))
return
b
.
String
()
}
func
jsIsSpecial
(
rune
int
)
bool
{
switch
rune
{
case
'\\'
,
'\'
'
,
'"'
:
return
true
}
return
rune
<
' '
||
utf8
.
RuneSelf
<=
rune
}
// JSEscaper returns the escaped JavaScript equivalent of the textual
// representation of its arguments.
func
JSEscaper
(
args
...
interface
{})
string
{
ok
:=
false
var
s
string
if
len
(
args
)
==
1
{
s
,
ok
=
args
[
0
]
.
(
string
)
}
if
!
ok
{
s
=
fmt
.
Sprint
(
args
...
)
}
return
JSEscapeString
(
s
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment