Source File
escape.go
Belonging Package
github.com/google/safehtml/template
package template
import (
)
if len() == 1 {
if , := [0].(string); {
return
}
}
for , := range {
[] = indirectToStringerOrError()
}
return fmt.Sprint(...)
}
actionNodeEdits map[*parse.ActionNode][]string
templateNodeEdits map[*parse.TemplateNode]string
textNodeEdits map[*parse.TextNode][]byte
}
func ( *escaper) ( context, parse.Node) context {
switch n := .(type) {
case *parse.ActionNode:
return .escapeAction(, )
case *parse.IfNode:
return .escapeBranch(, &.BranchNode, "if")
case *parse.ListNode:
return .escapeList(, )
case *parse.RangeNode:
return .escapeBranch(, &.BranchNode, "range")
case *parse.TemplateNode:
return .escapeTemplate(, )
case *parse.TextNode:
return .escapeText(, )
case *parse.WithNode:
return .escapeBranch(, &.BranchNode, "with")
}
panic("escaping " + .String() + " is unimplemented")
}
func ( *escaper) ( context, *parse.ActionNode) context {
return
}
for , := range .Pipe.Cmds {
, := .Args[0].(*parse.IdentifierNode)
continue
}
:= .Ident
if , := predefinedEscapers[]; {
if < len(.Pipe.Cmds)-1 ||
.state == stateAttr && .delim == delimSpaceOrTagEnd && == "html" {
return context{
state: stateError,
err: errorf(ErrPredefinedEscaper, , .Line, "predefined escaper %q disallowed in template", ),
}
}
}
}
switch .state {
case stateError:
return
case stateAttrName, stateTag:
.state = stateAttrName
, := sanitizerForContext()
if != nil {
return context{
err: errorf(ErrEscapeAction, , .Line, "cannot escape action %v: %s", , ),
}
}
.editActionNode(, )
return
}
return
.Args[0] = parse.NewIdentifier(evalArgsFuncName).SetTree(nil).SetPos(.Args[0].Position())
.Cmds = append(.Cmds, newIdentCmd(, .Position()))
++
--
}
}
}
:= make([]*parse.CommandNode, , +len())
copy(, .Cmds)
for , := range {
= append(, newIdentCmd(, .Position()))
}
.Cmds =
}
var predefinedEscapers = map[string]bool{
"html": true,
"urlquery": true,
}
sanitizeHTMLFuncName: "html",
normalizeURLFuncName: "urlquery",
}
func (, string) bool {
return normalizeEscFn() == normalizeEscFn()
}
func ( string) string {
if := equivEscapers[]; != "" {
return
}
return
}
func ( string, parse.Pos) *parse.CommandNode {
return &parse.CommandNode{
NodeType: parse.NodeCommand,
Args: []parse.Node{parse.NewIdentifier().SetTree(nil).SetPos()}, // TODO: SetTree.
Pos: ,
}
}
.state = stateAttrName
}
return
}
func (, context, parse.Node, string) context {
if .state == stateError {
return
}
if .state == stateError {
return
}
return
}
if , := nudge(), nudge(); !(.eq() && .eq()) {
if := (, , , ); .state != stateError {
return
}
}
return context{
state: stateError,
err: errorf(ErrBranchEnd, , 0, "{{%s}} branches end in different contexts: %v, %v", , , ),
}
}
func ( *escaper) ( context, *parse.BranchNode, string) context {
:= .escapeList(, .List)
, := .escapeListConditionally(, .List, nil)
= join(, , , )
.err.Line = .Line
.err.Description = "on range loop re-entry: " + .err.Description
return
}
}
:= .escapeList(, .ElseList)
return join(, , , )
}
for , := range .output {
.output[] =
}
= .escapeList(, )
:= != nil && (&, )
for , := range .output {
.output[] =
}
for , := range .derived {
.derived[] =
}
for , := range .called {
.called[] =
}
for , := range .actionNodeEdits {
.editActionNode(, )
}
for , := range .templateNodeEdits {
.editTemplateNode(, )
}
for , := range .textNodeEdits {
.editTextNode(, )
}
}
return ,
}
func ( *escaper) ( context, *parse.TemplateNode) context {
, := .escapeTree(, , .Name, .Line)
if != .Name {
.editTemplateNode(, )
}
return
}
return ,
}
:= .template()
if .ns.set[] != nil {
return context{
state: stateError,
err: errorf(ErrNoSuchTemplate, , , "%q is an incomplete or empty template", ),
},
}
return context{
state: stateError,
err: errorf(ErrNoSuchTemplate, , , "no such template %q", ),
},
}
, := .escapeTemplateBody(, )
if , := .escapeTemplateBody(, ); {
, = , true
}
if ! && .state != stateError {
return context{
state: stateError,
err: errorf(ErrOutputContext, .Tree.Root, 0, "cannot compute output context for template %s", .Name()),
}
}
return
}
return false
}
return true
return .eq()
.output[.Name()] =
return .escapeListConditionally(, .Tree.Root, )
}
var delimEnds = [...]string{
delimDoubleQuote: `"`,
delimSpaceOrTagEnd: " \t\n\f\r>",
}
var doctypeBytes = []byte("<!DOCTYPE")
return context{
state: stateError,
err: errorf(ErrCSPCompatibility, , 0, `"javascript:" URI disallowed for CSP compatibility`),
}
}
for != len() {
if .ns.cspCompatible && strings.HasPrefix(.attr.name, "on") {
return context{
state: stateError,
err: errorf(ErrCSPCompatibility, , 0, "inline event handler %q is disallowed for CSP compatibility", .attr.name),
}
}
, := contextAfterText(, [:])
:= +
, := sanitizationContextForElementContent(.element.name)
if .state == stateText || == nil && == sanitizationContextRCDATA {
:=
if .state != .state {
for := - 1; >= ; -- {
if [] == '<' {
=
break
}
}
}
for := ; < ; ++ {
if [] == '<' && !bytes.HasPrefix(bytes.ToUpper([:]), doctypeBytes) {
.Write([:])
.WriteString("<")
= + 1
}
}
} else if isComment(.state) && .delim == delimNone {
=
}
if .state == stateSpecialElementBody && .element.name == "script" {
if := isJsTemplateBalanced(bytes.NewBuffer()); != nil {
return context{
state: stateError,
err: errorf(ErrUnbalancedJsTemplate, , 0, "Mixing template systems can cause security vulnerabilites. Therefore, there can be no safehtml/template insertion points or actions inside an ES6 template, and all ES6 templates must be closed: %v", .Error()),
}
}
}
:= - 2
return , 0
return transitionFunc[.state](, [:])
}
for := []byte(html.UnescapeString(string())); len() != 0; {
, := transitionFunc[.state](, )
, = , [:]
}
return , len()
}
:= context{
state: stateTag,
element: .element,
scriptType: .scriptType,
linkRel: .linkRel,
++
}
return ,
}
func ( *escaper) ( *parse.ActionNode, []string) {
if , := .actionNodeEdits[]; {
panic(fmt.Sprintf("node %s shared between templates", ))
}
.actionNodeEdits[] =
}
func ( *escaper) ( *parse.TemplateNode, string) {
if , := .templateNodeEdits[]; {
panic(fmt.Sprintf("node %s shared between templates", ))
}
.templateNodeEdits[] =
}
func ( *escaper) ( *parse.TextNode, []byte) {
if , := .textNodeEdits[]; {
panic(fmt.Sprintf("node %s shared between templates", ))
}
.textNodeEdits[] =
}
:= .arbitraryTemplate()
for , := range .derived {
if , := .text.AddParseTree(.Name(), .Tree); != nil {
panic("error adding derived template")
}
}
for , := range .actionNodeEdits {
ensurePipelineContains(.Pipe, )
}
for , := range .templateNodeEdits {
.Name =
}
for , := range .textNodeEdits {
.Text =
.called = make(map[string]bool)
.actionNodeEdits = make(map[*parse.ActionNode][]string)
.templateNodeEdits = make(map[*parse.TemplateNode]string)
.textNodeEdits = make(map[*parse.TextNode][]byte)
}
:= .arbitraryTemplate().text.Lookup()
if == nil {
= .derived[]
}
return
}
func ( interface{}) interface{} {
if == nil {
return nil
}
:= reflect.ValueOf()
for !.Type().Implements(fmtStringerType) && !.Type().Implements(errorType) && .Kind() == reflect.Ptr && !.IsNil() {
= .Elem()
}
return .Interface()
}
var (
jsTemplateSeparator = []byte("`")
jsTemplateExprStart = []byte("${")
jsTemplateExprEnd = []byte("}")
)
func ( *bytes.Buffer) error {
for {
:= bytes.Index(.Bytes(), jsTemplateSeparator)
:= bytes.Index(.Bytes(), jsTemplateExprStart)
if == -1 {
return fmt.Errorf("Missing closing ` in JS template")
}
if != -1 && < {
:= consumeJsTemplateExpr()
if != nil {
return
}
return ()
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |