mirror of
https://git.yoctoproject.org/poky
synced 2026-03-19 13:49:41 +01:00
go: Update fix for CVE-2023-24538 & CVE-2023-39318
Add missing files in fix for CVE-2023-24538 & CVE-2023-39318 Upstream Link - CVE-2023-24538:b1e3ecfa06CVE-2023-39318:023b542edf(From OE-Core rev: 0d8f7062d4fb5525f34427b1a7304f165bee0cfc) Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
This commit is contained in:
committed by
Steve Sakoman
parent
25073f9c0e
commit
74b22a5e91
@@ -29,7 +29,8 @@ SRC_URI += "\
|
||||
file://CVE-2022-41722.patch \
|
||||
file://CVE-2023-24537.patch \
|
||||
file://CVE-2023-24534.patch \
|
||||
file://CVE-2023-24538.patch \
|
||||
file://CVE-2023-24538_1.patch \
|
||||
file://CVE-2023-24538_2.patch \
|
||||
file://CVE-2023-24540.patch \
|
||||
file://CVE-2023-24539.patch \
|
||||
file://CVE-2023-29404.patch \
|
||||
|
||||
597
meta/recipes-devtools/go/go-1.18/CVE-2023-24538_1.patch
Normal file
597
meta/recipes-devtools/go/go-1.18/CVE-2023-24538_1.patch
Normal file
@@ -0,0 +1,597 @@
|
||||
From b1e4e8ec7e946ff2d3bb37ac99c5468ceb49c362 Mon Sep 17 00:00:00 2001
|
||||
From: Russ Cox <rsc@golang.org>
|
||||
Date: Thu, 20 May 2021 12:46:33 -0400
|
||||
Subject: [PATCH 1/2] html/template, text/template: implement break and
|
||||
continue for range loops
|
||||
|
||||
Break and continue for range loops was accepted as a proposal in June 2017.
|
||||
It was implemented in CL 66410 (Oct 2017)
|
||||
but then rolled back in CL 92155 (Feb 2018)
|
||||
because html/template changes had not been implemented.
|
||||
|
||||
This CL reimplements break and continue in text/template
|
||||
and then adds support for them in html/template as well.
|
||||
|
||||
Fixes #20531.
|
||||
|
||||
Change-Id: I05330482a976f1c078b4b49c2287bd9031bb7616
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/321491
|
||||
Trust: Russ Cox <rsc@golang.org>
|
||||
Run-TryBot: Russ Cox <rsc@golang.org>
|
||||
TryBot-Result: Go Bot <gobot@golang.org>
|
||||
Reviewed-by: Rob Pike <r@golang.org>
|
||||
|
||||
Upstream-Status: Backport from https://github.com/golang/go/commit/d0dd26a88c019d54f22463daae81e785f5867565
|
||||
CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
src/html/template/context.go | 4 ++
|
||||
src/html/template/escape.go | 71 ++++++++++++++++++++++++++++++++++-
|
||||
src/html/template/escape_test.go | 24 ++++++++++++
|
||||
src/html/template/exec_test.go | 2 +
|
||||
src/text/template/doc.go | 8 ++++
|
||||
src/text/template/exec.go | 24 +++++++++++-
|
||||
src/text/template/exec_test.go | 2 +
|
||||
src/text/template/parse/lex.go | 13 ++++++-
|
||||
src/text/template/parse/lex_test.go | 2 +
|
||||
src/text/template/parse/node.go | 36 ++++++++++++++++++
|
||||
src/text/template/parse/parse.go | 42 ++++++++++++++++++++-
|
||||
src/text/template/parse/parse_test.go | 8 ++++
|
||||
12 files changed, 232 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/context.go b/src/html/template/context.go
|
||||
index f7d4849..aaa7d08 100644
|
||||
--- a/src/html/template/context.go
|
||||
+++ b/src/html/template/context.go
|
||||
@@ -6,6 +6,7 @@ package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+ "text/template/parse"
|
||||
)
|
||||
|
||||
// context describes the state an HTML parser must be in when it reaches the
|
||||
@@ -22,6 +23,7 @@ type context struct {
|
||||
jsCtx jsCtx
|
||||
attr attr
|
||||
element element
|
||||
+ n parse.Node // for range break/continue
|
||||
err *Error
|
||||
}
|
||||
|
||||
@@ -141,6 +143,8 @@ const (
|
||||
// stateError is an infectious error state outside any valid
|
||||
// HTML/CSS/JS construct.
|
||||
stateError
|
||||
+ // stateDead marks unreachable code after a {{break}} or {{continue}}.
|
||||
+ stateDead
|
||||
)
|
||||
|
||||
// isComment is true for any state that contains content meant for template
|
||||
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
|
||||
index 8739735..6dea79c 100644
|
||||
--- a/src/html/template/escape.go
|
||||
+++ b/src/html/template/escape.go
|
||||
@@ -97,6 +97,15 @@ type escaper struct {
|
||||
actionNodeEdits map[*parse.ActionNode][]string
|
||||
templateNodeEdits map[*parse.TemplateNode]string
|
||||
textNodeEdits map[*parse.TextNode][]byte
|
||||
+ // rangeContext holds context about the current range loop.
|
||||
+ rangeContext *rangeContext
|
||||
+}
|
||||
+
|
||||
+// rangeContext holds information about the current range loop.
|
||||
+type rangeContext struct {
|
||||
+ outer *rangeContext // outer loop
|
||||
+ breaks []context // context at each break action
|
||||
+ continues []context // context at each continue action
|
||||
}
|
||||
|
||||
// makeEscaper creates a blank escaper for the given set.
|
||||
@@ -109,6 +118,7 @@ func makeEscaper(n *nameSpace) escaper {
|
||||
map[*parse.ActionNode][]string{},
|
||||
map[*parse.TemplateNode]string{},
|
||||
map[*parse.TextNode][]byte{},
|
||||
+ nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +134,16 @@ func (e *escaper) escape(c context, n parse.Node) context {
|
||||
switch n := n.(type) {
|
||||
case *parse.ActionNode:
|
||||
return e.escapeAction(c, n)
|
||||
+ case *parse.BreakNode:
|
||||
+ c.n = n
|
||||
+ e.rangeContext.breaks = append(e.rangeContext.breaks, c)
|
||||
+ return context{state: stateDead}
|
||||
case *parse.CommentNode:
|
||||
return c
|
||||
+ case *parse.ContinueNode:
|
||||
+ c.n = n
|
||||
+ e.rangeContext.continues = append(e.rangeContext.breaks, c)
|
||||
+ return context{state: stateDead}
|
||||
case *parse.IfNode:
|
||||
return e.escapeBranch(c, &n.BranchNode, "if")
|
||||
case *parse.ListNode:
|
||||
@@ -427,6 +445,12 @@ func join(a, b context, node parse.Node, nodeName string) context {
|
||||
if b.state == stateError {
|
||||
return b
|
||||
}
|
||||
+ if a.state == stateDead {
|
||||
+ return b
|
||||
+ }
|
||||
+ if b.state == stateDead {
|
||||
+ return a
|
||||
+ }
|
||||
if a.eq(b) {
|
||||
return a
|
||||
}
|
||||
@@ -466,14 +490,27 @@ func join(a, b context, node parse.Node, nodeName string) context {
|
||||
|
||||
// escapeBranch escapes a branch template node: "if", "range" and "with".
|
||||
func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context {
|
||||
+ if nodeName == "range" {
|
||||
+ e.rangeContext = &rangeContext{outer: e.rangeContext}
|
||||
+ }
|
||||
c0 := e.escapeList(c, n.List)
|
||||
- if nodeName == "range" && c0.state != stateError {
|
||||
+ if nodeName == "range" {
|
||||
+ if c0.state != stateError {
|
||||
+ c0 = joinRange(c0, e.rangeContext)
|
||||
+ }
|
||||
+ e.rangeContext = e.rangeContext.outer
|
||||
+ if c0.state == stateError {
|
||||
+ return c0
|
||||
+ }
|
||||
+
|
||||
// The "true" branch of a "range" node can execute multiple times.
|
||||
// We check that executing n.List once results in the same context
|
||||
// as executing n.List twice.
|
||||
+ e.rangeContext = &rangeContext{outer: e.rangeContext}
|
||||
c1, _ := e.escapeListConditionally(c0, n.List, nil)
|
||||
c0 = join(c0, c1, n, nodeName)
|
||||
if c0.state == stateError {
|
||||
+ e.rangeContext = e.rangeContext.outer
|
||||
// Make clear that this is a problem on loop re-entry
|
||||
// since developers tend to overlook that branch when
|
||||
// debugging templates.
|
||||
@@ -481,11 +518,39 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string)
|
||||
c0.err.Description = "on range loop re-entry: " + c0.err.Description
|
||||
return c0
|
||||
}
|
||||
+ c0 = joinRange(c0, e.rangeContext)
|
||||
+ e.rangeContext = e.rangeContext.outer
|
||||
+ if c0.state == stateError {
|
||||
+ return c0
|
||||
+ }
|
||||
}
|
||||
c1 := e.escapeList(c, n.ElseList)
|
||||
return join(c0, c1, n, nodeName)
|
||||
}
|
||||
|
||||
+func joinRange(c0 context, rc *rangeContext) context {
|
||||
+ // Merge contexts at break and continue statements into overall body context.
|
||||
+ // In theory we could treat breaks differently from continues, but for now it is
|
||||
+ // enough to treat them both as going back to the start of the loop (which may then stop).
|
||||
+ for _, c := range rc.breaks {
|
||||
+ c0 = join(c0, c, c.n, "range")
|
||||
+ if c0.state == stateError {
|
||||
+ c0.err.Line = c.n.(*parse.BreakNode).Line
|
||||
+ c0.err.Description = "at range loop break: " + c0.err.Description
|
||||
+ return c0
|
||||
+ }
|
||||
+ }
|
||||
+ for _, c := range rc.continues {
|
||||
+ c0 = join(c0, c, c.n, "range")
|
||||
+ if c0.state == stateError {
|
||||
+ c0.err.Line = c.n.(*parse.ContinueNode).Line
|
||||
+ c0.err.Description = "at range loop continue: " + c0.err.Description
|
||||
+ return c0
|
||||
+ }
|
||||
+ }
|
||||
+ return c0
|
||||
+}
|
||||
+
|
||||
// escapeList escapes a list template node.
|
||||
func (e *escaper) escapeList(c context, n *parse.ListNode) context {
|
||||
if n == nil {
|
||||
@@ -493,6 +558,9 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context {
|
||||
}
|
||||
for _, m := range n.Nodes {
|
||||
c = e.escape(c, m)
|
||||
+ if c.state == stateDead {
|
||||
+ break
|
||||
+ }
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -503,6 +571,7 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context {
|
||||
// which is the same as whether e was updated.
|
||||
func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) {
|
||||
e1 := makeEscaper(e.ns)
|
||||
+ e1.rangeContext = e.rangeContext
|
||||
// Make type inferences available to f.
|
||||
for k, v := range e.output {
|
||||
e1.output[k] = v
|
||||
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
|
||||
index fbc84a7..3b0aa8c 100644
|
||||
--- a/src/html/template/escape_test.go
|
||||
+++ b/src/html/template/escape_test.go
|
||||
@@ -920,6 +920,22 @@ func TestErrors(t *testing.T) {
|
||||
"<a href='/foo?{{range .Items}}&{{.K}}={{.V}}{{end}}'>",
|
||||
"",
|
||||
},
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{continue}}{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{break}}{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
// Error cases.
|
||||
{
|
||||
"{{if .Cond}}<a{{end}}",
|
||||
@@ -956,6 +972,14 @@ func TestErrors(t *testing.T) {
|
||||
"z:2:8: on range loop re-entry: {{range}} branches",
|
||||
},
|
||||
{
|
||||
+ "{{range .Items}}<a{{if .X}}{{break}}{{end}}>{{end}}",
|
||||
+ "z:1:29: at range loop break: {{range}} branches end in different contexts",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{continue}}{{end}}>{{end}}",
|
||||
+ "z:1:29: at range loop continue: {{range}} branches end in different contexts",
|
||||
+ },
|
||||
+ {
|
||||
"<a b=1 c={{.H}}",
|
||||
"z: ends in a non-text context: {stateAttr delimSpaceOrTagEnd",
|
||||
},
|
||||
diff --git a/src/html/template/exec_test.go b/src/html/template/exec_test.go
|
||||
index 8885873..523340b 100644
|
||||
--- a/src/html/template/exec_test.go
|
||||
+++ b/src/html/template/exec_test.go
|
||||
@@ -567,6 +567,8 @@ var execTests = []execTest{
|
||||
{"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
|
||||
{"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
|
||||
{"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||
+ {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true},
|
||||
+ {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
|
||||
{"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
|
||||
{"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
|
||||
{"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true},
|
||||
diff --git a/src/text/template/doc.go b/src/text/template/doc.go
|
||||
index 7b30294..0228b15 100644
|
||||
--- a/src/text/template/doc.go
|
||||
+++ b/src/text/template/doc.go
|
||||
@@ -112,6 +112,14 @@ data, defined in detail in the corresponding sections that follow.
|
||||
T0 is executed; otherwise, dot is set to the successive elements
|
||||
of the array, slice, or map and T1 is executed.
|
||||
|
||||
+ {{break}}
|
||||
+ The innermost {{range pipeline}} loop is ended early, stopping the
|
||||
+ current iteration and bypassing all remaining iterations.
|
||||
+
|
||||
+ {{continue}}
|
||||
+ The current iteration of the innermost {{range pipeline}} loop is
|
||||
+ stopped, and the loop starts the next iteration.
|
||||
+
|
||||
{{template "name"}}
|
||||
The template with the specified name is executed with nil data.
|
||||
|
||||
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
|
||||
index 5ad3b4e..92fa9d9 100644
|
||||
--- a/src/text/template/exec.go
|
||||
+++ b/src/text/template/exec.go
|
||||
@@ -5,6 +5,7 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
+ "errors"
|
||||
"fmt"
|
||||
"internal/fmtsort"
|
||||
"io"
|
||||
@@ -243,6 +244,12 @@ func (t *Template) DefinedTemplates() string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
+// Sentinel errors for use with panic to signal early exits from range loops.
|
||||
+var (
|
||||
+ walkBreak = errors.New("break")
|
||||
+ walkContinue = errors.New("continue")
|
||||
+)
|
||||
+
|
||||
// Walk functions step through the major pieces of the template structure,
|
||||
// generating output as they go.
|
||||
func (s *state) walk(dot reflect.Value, node parse.Node) {
|
||||
@@ -255,7 +262,11 @@ func (s *state) walk(dot reflect.Value, node parse.Node) {
|
||||
if len(node.Pipe.Decl) == 0 {
|
||||
s.printValue(node, val)
|
||||
}
|
||||
+ case *parse.BreakNode:
|
||||
+ panic(walkBreak)
|
||||
case *parse.CommentNode:
|
||||
+ case *parse.ContinueNode:
|
||||
+ panic(walkContinue)
|
||||
case *parse.IfNode:
|
||||
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
|
||||
case *parse.ListNode:
|
||||
@@ -334,6 +345,11 @@ func isTrue(val reflect.Value) (truth, ok bool) {
|
||||
|
||||
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
||||
s.at(r)
|
||||
+ defer func() {
|
||||
+ if r := recover(); r != nil && r != walkBreak {
|
||||
+ panic(r)
|
||||
+ }
|
||||
+ }()
|
||||
defer s.pop(s.mark())
|
||||
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
|
||||
// mark top of stack before any variables in the body are pushed.
|
||||
@@ -347,8 +363,14 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
||||
if len(r.Pipe.Decl) > 1 {
|
||||
s.setTopVar(2, index)
|
||||
}
|
||||
+ defer s.pop(mark)
|
||||
+ defer func() {
|
||||
+ // Consume panic(walkContinue)
|
||||
+ if r := recover(); r != nil && r != walkContinue {
|
||||
+ panic(r)
|
||||
+ }
|
||||
+ }()
|
||||
s.walk(elem, r.List)
|
||||
- s.pop(mark)
|
||||
}
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
|
||||
index ef52164..586af55 100644
|
||||
--- a/src/text/template/exec_test.go
|
||||
+++ b/src/text/template/exec_test.go
|
||||
@@ -564,6 +564,8 @@ var execTests = []execTest{
|
||||
{"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
|
||||
{"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
|
||||
{"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||
+ {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true},
|
||||
+ {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
|
||||
{"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
|
||||
{"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
|
||||
{"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true},
|
||||
diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go
|
||||
index 6784071..95e3377 100644
|
||||
--- a/src/text/template/parse/lex.go
|
||||
+++ b/src/text/template/parse/lex.go
|
||||
@@ -62,6 +62,8 @@ const (
|
||||
// Keywords appear after all the rest.
|
||||
itemKeyword // used only to delimit the keywords
|
||||
itemBlock // block keyword
|
||||
+ itemBreak // break keyword
|
||||
+ itemContinue // continue keyword
|
||||
itemDot // the cursor, spelled '.'
|
||||
itemDefine // define keyword
|
||||
itemElse // else keyword
|
||||
@@ -76,6 +78,8 @@ const (
|
||||
var key = map[string]itemType{
|
||||
".": itemDot,
|
||||
"block": itemBlock,
|
||||
+ "break": itemBreak,
|
||||
+ "continue": itemContinue,
|
||||
"define": itemDefine,
|
||||
"else": itemElse,
|
||||
"end": itemEnd,
|
||||
@@ -119,6 +123,8 @@ type lexer struct {
|
||||
parenDepth int // nesting depth of ( ) exprs
|
||||
line int // 1+number of newlines seen
|
||||
startLine int // start line of this item
|
||||
+ breakOK bool // break keyword allowed
|
||||
+ continueOK bool // continue keyword allowed
|
||||
}
|
||||
|
||||
// next returns the next rune in the input.
|
||||
@@ -461,7 +467,12 @@ Loop:
|
||||
}
|
||||
switch {
|
||||
case key[word] > itemKeyword:
|
||||
- l.emit(key[word])
|
||||
+ item := key[word]
|
||||
+ if item == itemBreak && !l.breakOK || item == itemContinue && !l.continueOK {
|
||||
+ l.emit(itemIdentifier)
|
||||
+ } else {
|
||||
+ l.emit(item)
|
||||
+ }
|
||||
case word[0] == '.':
|
||||
l.emit(itemField)
|
||||
case word == "true", word == "false":
|
||||
diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go
|
||||
index 6510eed..df6aabf 100644
|
||||
--- a/src/text/template/parse/lex_test.go
|
||||
+++ b/src/text/template/parse/lex_test.go
|
||||
@@ -35,6 +35,8 @@ var itemName = map[itemType]string{
|
||||
// keywords
|
||||
itemDot: ".",
|
||||
itemBlock: "block",
|
||||
+ itemBreak: "break",
|
||||
+ itemContinue: "continue",
|
||||
itemDefine: "define",
|
||||
itemElse: "else",
|
||||
itemIf: "if",
|
||||
diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go
|
||||
index 177482f..4726822 100644
|
||||
--- a/src/text/template/parse/node.go
|
||||
+++ b/src/text/template/parse/node.go
|
||||
@@ -71,6 +71,8 @@ const (
|
||||
NodeVariable // A $ variable.
|
||||
NodeWith // A with action.
|
||||
NodeComment // A comment.
|
||||
+ NodeBreak // A break action.
|
||||
+ NodeContinue // A continue action.
|
||||
)
|
||||
|
||||
// Nodes.
|
||||
@@ -907,6 +909,40 @@ func (i *IfNode) Copy() Node {
|
||||
return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
|
||||
}
|
||||
|
||||
+// BreakNode represents a {{break}} action.
|
||||
+type BreakNode struct {
|
||||
+ tr *Tree
|
||||
+ NodeType
|
||||
+ Pos
|
||||
+ Line int
|
||||
+}
|
||||
+
|
||||
+func (t *Tree) newBreak(pos Pos, line int) *BreakNode {
|
||||
+ return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line}
|
||||
+}
|
||||
+
|
||||
+func (b *BreakNode) Copy() Node { return b.tr.newBreak(b.Pos, b.Line) }
|
||||
+func (b *BreakNode) String() string { return "{{break}}" }
|
||||
+func (b *BreakNode) tree() *Tree { return b.tr }
|
||||
+func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") }
|
||||
+
|
||||
+// ContinueNode represents a {{continue}} action.
|
||||
+type ContinueNode struct {
|
||||
+ tr *Tree
|
||||
+ NodeType
|
||||
+ Pos
|
||||
+ Line int
|
||||
+}
|
||||
+
|
||||
+func (t *Tree) newContinue(pos Pos, line int) *ContinueNode {
|
||||
+ return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line}
|
||||
+}
|
||||
+
|
||||
+func (c *ContinueNode) Copy() Node { return c.tr.newContinue(c.Pos, c.Line) }
|
||||
+func (c *ContinueNode) String() string { return "{{continue}}" }
|
||||
+func (c *ContinueNode) tree() *Tree { return c.tr }
|
||||
+func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") }
|
||||
+
|
||||
// RangeNode represents a {{range}} action and its commands.
|
||||
type RangeNode struct {
|
||||
BranchNode
|
||||
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
|
||||
index 1a63961..d92bed5 100644
|
||||
--- a/src/text/template/parse/parse.go
|
||||
+++ b/src/text/template/parse/parse.go
|
||||
@@ -31,6 +31,7 @@ type Tree struct {
|
||||
vars []string // variables defined at the moment.
|
||||
treeSet map[string]*Tree
|
||||
actionLine int // line of left delim starting action
|
||||
+ rangeDepth int
|
||||
mode Mode
|
||||
}
|
||||
|
||||
@@ -224,6 +225,8 @@ func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer, treeSet ma
|
||||
t.vars = []string{"$"}
|
||||
t.funcs = funcs
|
||||
t.treeSet = treeSet
|
||||
+ lex.breakOK = !t.hasFunction("break")
|
||||
+ lex.continueOK = !t.hasFunction("continue")
|
||||
}
|
||||
|
||||
// stopParse terminates parsing.
|
||||
@@ -386,6 +389,10 @@ func (t *Tree) action() (n Node) {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
case itemBlock:
|
||||
return t.blockControl()
|
||||
+ case itemBreak:
|
||||
+ return t.breakControl(token.pos, token.line)
|
||||
+ case itemContinue:
|
||||
+ return t.continueControl(token.pos, token.line)
|
||||
case itemElse:
|
||||
return t.elseControl()
|
||||
case itemEnd:
|
||||
@@ -405,6 +412,32 @@ func (t *Tree) action() (n Node) {
|
||||
return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim))
|
||||
}
|
||||
|
||||
+// Break:
|
||||
+// {{break}}
|
||||
+// Break keyword is past.
|
||||
+func (t *Tree) breakControl(pos Pos, line int) Node {
|
||||
+ if token := t.next(); token.typ != itemRightDelim {
|
||||
+ t.unexpected(token, "in {{break}}")
|
||||
+ }
|
||||
+ if t.rangeDepth == 0 {
|
||||
+ t.errorf("{{break}} outside {{range}}")
|
||||
+ }
|
||||
+ return t.newBreak(pos, line)
|
||||
+}
|
||||
+
|
||||
+// Continue:
|
||||
+// {{continue}}
|
||||
+// Continue keyword is past.
|
||||
+func (t *Tree) continueControl(pos Pos, line int) Node {
|
||||
+ if token := t.next(); token.typ != itemRightDelim {
|
||||
+ t.unexpected(token, "in {{continue}}")
|
||||
+ }
|
||||
+ if t.rangeDepth == 0 {
|
||||
+ t.errorf("{{continue}} outside {{range}}")
|
||||
+ }
|
||||
+ return t.newContinue(pos, line)
|
||||
+}
|
||||
+
|
||||
// Pipeline:
|
||||
// declarations? command ('|' command)*
|
||||
func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
|
||||
@@ -480,8 +513,14 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
|
||||
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
|
||||
defer t.popVars(len(t.vars))
|
||||
pipe = t.pipeline(context, itemRightDelim)
|
||||
+ if context == "range" {
|
||||
+ t.rangeDepth++
|
||||
+ }
|
||||
var next Node
|
||||
list, next = t.itemList()
|
||||
+ if context == "range" {
|
||||
+ t.rangeDepth--
|
||||
+ }
|
||||
switch next.Type() {
|
||||
case nodeEnd: //done
|
||||
case nodeElse:
|
||||
@@ -523,7 +562,8 @@ func (t *Tree) ifControl() Node {
|
||||
// {{range pipeline}} itemList {{else}} itemList {{end}}
|
||||
// Range keyword is past.
|
||||
func (t *Tree) rangeControl() Node {
|
||||
- return t.newRange(t.parseControl(false, "range"))
|
||||
+ r := t.newRange(t.parseControl(false, "range"))
|
||||
+ return r
|
||||
}
|
||||
|
||||
// With:
|
||||
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
|
||||
index 9b1be27..c3679a0 100644
|
||||
--- a/src/text/template/parse/parse_test.go
|
||||
+++ b/src/text/template/parse/parse_test.go
|
||||
@@ -230,6 +230,10 @@ var parseTests = []parseTest{
|
||||
`{{range $x := .SI}}{{.}}{{end}}`},
|
||||
{"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
|
||||
`{{range $x, $y := .SI}}{{.}}{{end}}`},
|
||||
+ {"range with break", "{{range .SI}}{{.}}{{break}}{{end}}", noError,
|
||||
+ `{{range .SI}}{{.}}{{break}}{{end}}`},
|
||||
+ {"range with continue", "{{range .SI}}{{.}}{{continue}}{{end}}", noError,
|
||||
+ `{{range .SI}}{{.}}{{continue}}{{end}}`},
|
||||
{"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
|
||||
`{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
|
||||
{"template", "{{template `x`}}", noError,
|
||||
@@ -279,6 +283,10 @@ var parseTests = []parseTest{
|
||||
{"adjacent args", "{{printf 3`x`}}", hasError, ""},
|
||||
{"adjacent args with .", "{{printf `x`.}}", hasError, ""},
|
||||
{"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""},
|
||||
+ {"break outside range", "{{range .}}{{end}} {{break}}", hasError, ""},
|
||||
+ {"continue outside range", "{{range .}}{{end}} {{continue}}", hasError, ""},
|
||||
+ {"break in range else", "{{range .}}{{else}}{{break}}{{end}}", hasError, ""},
|
||||
+ {"continue in range else", "{{range .}}{{else}}{{continue}}{{end}}", hasError, ""},
|
||||
// Other kinds of assignments and operators aren't available yet.
|
||||
{"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
|
||||
{"bug0b", "{{$x += 1}}{{$x}}", hasError, ""},
|
||||
--
|
||||
2.7.4
|
||||
@@ -1,7 +1,7 @@
|
||||
From 07cc3b8711a8efbb5885f56dd90d854049ad2f7d Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <bracewell@google.com>
|
||||
Date: Mon, 20 Mar 2023 11:01:13 -0700
|
||||
Subject: [PATCH] html/template: disallow actions in JS template literals
|
||||
Subject: [PATCH 2/2] html/template: disallow actions in JS template literals
|
||||
|
||||
ECMAScript 6 introduced template literals[0][1] which are delimited with
|
||||
backticks. These need to be escaped in a similar fashion to the
|
||||
@@ -52,12 +52,15 @@ CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
src/html/template/context.go | 2 ++
|
||||
src/html/template/error.go | 13 +++++++++++++
|
||||
src/html/template/escape.go | 11 +++++++++++
|
||||
src/html/template/error.go | 13 ++++++++
|
||||
src/html/template/escape.go | 11 +++++++
|
||||
src/html/template/escape_test.go | 66 ++++++++++++++++++++++-----------------
|
||||
src/html/template/js.go | 2 ++
|
||||
src/html/template/jsctx_string.go | 9 +++++++++
|
||||
src/html/template/transition.go | 7 ++++++-
|
||||
6 files changed, 43 insertions(+), 1 deletion(-)
|
||||
src/html/template/js_test.go | 2 +-
|
||||
src/html/template/jsctx_string.go | 9 ++++++
|
||||
src/html/template/state_string.go | 37 ++++++++++++++++++++--
|
||||
src/html/template/transition.go | 7 ++++-
|
||||
9 files changed, 116 insertions(+), 33 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/context.go b/src/html/template/context.go
|
||||
index f7d4849..0b65313 100644
|
||||
@@ -125,6 +128,104 @@ index 8739735..ca078f4 100644
|
||||
case stateJSRegexp:
|
||||
s = append(s, "_html_template_jsregexpescaper")
|
||||
case stateCSS:
|
||||
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
|
||||
index 3b0aa8c..a695b17 100644
|
||||
--- a/src/html/template/escape_test.go
|
||||
+++ b/src/html/template/escape_test.go
|
||||
@@ -681,35 +681,31 @@ func TestEscape(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
- tmpl := New(test.name)
|
||||
- tmpl = Must(tmpl.Parse(test.input))
|
||||
- // Check for bug 6459: Tree field was not set in Parse.
|
||||
- if tmpl.Tree != tmpl.text.Tree {
|
||||
- t.Errorf("%s: tree not set properly", test.name)
|
||||
- continue
|
||||
- }
|
||||
- b := new(bytes.Buffer)
|
||||
- if err := tmpl.Execute(b, data); err != nil {
|
||||
- t.Errorf("%s: template execution failed: %s", test.name, err)
|
||||
- continue
|
||||
- }
|
||||
- if w, g := test.output, b.String(); w != g {
|
||||
- t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
- continue
|
||||
- }
|
||||
- b.Reset()
|
||||
- if err := tmpl.Execute(b, pdata); err != nil {
|
||||
- t.Errorf("%s: template execution failed for pointer: %s", test.name, err)
|
||||
- continue
|
||||
- }
|
||||
- if w, g := test.output, b.String(); w != g {
|
||||
- t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
- continue
|
||||
- }
|
||||
- if tmpl.Tree != tmpl.text.Tree {
|
||||
- t.Errorf("%s: tree mismatch", test.name)
|
||||
- continue
|
||||
- }
|
||||
+ t.Run(test.name, func(t *testing.T) {
|
||||
+ tmpl := New(test.name)
|
||||
+ tmpl = Must(tmpl.Parse(test.input))
|
||||
+ // Check for bug 6459: Tree field was not set in Parse.
|
||||
+ if tmpl.Tree != tmpl.text.Tree {
|
||||
+ t.Fatalf("%s: tree not set properly", test.name)
|
||||
+ }
|
||||
+ b := new(strings.Builder)
|
||||
+ if err := tmpl.Execute(b, data); err != nil {
|
||||
+ t.Fatalf("%s: template execution failed: %s", test.name, err)
|
||||
+ }
|
||||
+ if w, g := test.output, b.String(); w != g {
|
||||
+ t.Fatalf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
+ }
|
||||
+ b.Reset()
|
||||
+ if err := tmpl.Execute(b, pdata); err != nil {
|
||||
+ t.Fatalf("%s: template execution failed for pointer: %s", test.name, err)
|
||||
+ }
|
||||
+ if w, g := test.output, b.String(); w != g {
|
||||
+ t.Fatalf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
+ }
|
||||
+ if tmpl.Tree != tmpl.text.Tree {
|
||||
+ t.Fatalf("%s: tree mismatch", test.name)
|
||||
+ }
|
||||
+ })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,6 +932,10 @@ func TestErrors(t *testing.T) {
|
||||
"{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}",
|
||||
"",
|
||||
},
|
||||
+ {
|
||||
+ "<script>var a = `${a+b}`</script>`",
|
||||
+ "",
|
||||
+ },
|
||||
// Error cases.
|
||||
{
|
||||
"{{if .Cond}}<a{{end}}",
|
||||
@@ -1082,6 +1082,10 @@ func TestErrors(t *testing.T) {
|
||||
// html is allowed since it is the last command in the pipeline, but urlquery is not.
|
||||
`predefined escaper "urlquery" disallowed in template`,
|
||||
},
|
||||
+ {
|
||||
+ "<script>var tmpl = `asd {{.}}`;</script>",
|
||||
+ `{{.}} appears in a JS template literal`,
|
||||
+ },
|
||||
}
|
||||
for _, test := range tests {
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -1304,6 +1308,10 @@ func TestEscapeText(t *testing.T) {
|
||||
context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
|
||||
},
|
||||
{
|
||||
+ "<a onclick=\"`foo",
|
||||
+ context{state: stateJSBqStr, delim: delimDoubleQuote, attr: attrScript},
|
||||
+ },
|
||||
+ {
|
||||
`<A ONCLICK="'`,
|
||||
context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
|
||||
},
|
||||
diff --git a/src/html/template/js.go b/src/html/template/js.go
|
||||
index ea9c183..b888eaf 100644
|
||||
--- a/src/html/template/js.go
|
||||
@@ -145,6 +246,19 @@ index ea9c183..b888eaf 100644
|
||||
'+': `\u002b`,
|
||||
'/': `\/`,
|
||||
'<': `\u003c`,
|
||||
diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go
|
||||
index d7ee47b..7d963ae 100644
|
||||
--- a/src/html/template/js_test.go
|
||||
+++ b/src/html/template/js_test.go
|
||||
@@ -292,7 +292,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
|
||||
`0123456789:;\u003c=\u003e?` +
|
||||
`@ABCDEFGHIJKLMNO` +
|
||||
`PQRSTUVWXYZ[\\]^_` +
|
||||
- "`abcdefghijklmno" +
|
||||
+ "\\u0060abcdefghijklmno" +
|
||||
"pqrstuvwxyz{|}~\u007f" +
|
||||
"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
|
||||
},
|
||||
diff --git a/src/html/template/jsctx_string.go b/src/html/template/jsctx_string.go
|
||||
index dd1d87e..2394893 100644
|
||||
--- a/src/html/template/jsctx_string.go
|
||||
@@ -165,6 +279,55 @@ index dd1d87e..2394893 100644
|
||||
const _jsCtx_name = "jsCtxRegexpjsCtxDivOpjsCtxUnknown"
|
||||
|
||||
var _jsCtx_index = [...]uint8{0, 11, 21, 33}
|
||||
diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go
|
||||
index 05104be..6fb1a6e 100644
|
||||
--- a/src/html/template/state_string.go
|
||||
+++ b/src/html/template/state_string.go
|
||||
@@ -4,9 +4,42 @@ package template
|
||||
|
||||
import "strconv"
|
||||
|
||||
-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError"
|
||||
+func _() {
|
||||
+ // An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
+ // Re-run the stringer command to generate them again.
|
||||
+ var x [1]struct{}
|
||||
+ _ = x[stateText-0]
|
||||
+ _ = x[stateTag-1]
|
||||
+ _ = x[stateAttrName-2]
|
||||
+ _ = x[stateAfterName-3]
|
||||
+ _ = x[stateBeforeValue-4]
|
||||
+ _ = x[stateHTMLCmt-5]
|
||||
+ _ = x[stateRCDATA-6]
|
||||
+ _ = x[stateAttr-7]
|
||||
+ _ = x[stateURL-8]
|
||||
+ _ = x[stateSrcset-9]
|
||||
+ _ = x[stateJS-10]
|
||||
+ _ = x[stateJSDqStr-11]
|
||||
+ _ = x[stateJSSqStr-12]
|
||||
+ _ = x[stateJSBqStr-13]
|
||||
+ _ = x[stateJSRegexp-14]
|
||||
+ _ = x[stateJSBlockCmt-15]
|
||||
+ _ = x[stateJSLineCmt-16]
|
||||
+ _ = x[stateCSS-17]
|
||||
+ _ = x[stateCSSDqStr-18]
|
||||
+ _ = x[stateCSSSqStr-19]
|
||||
+ _ = x[stateCSSDqURL-20]
|
||||
+ _ = x[stateCSSSqURL-21]
|
||||
+ _ = x[stateCSSURL-22]
|
||||
+ _ = x[stateCSSBlockCmt-23]
|
||||
+ _ = x[stateCSSLineCmt-24]
|
||||
+ _ = x[stateError-25]
|
||||
+ _ = x[stateDead-26]
|
||||
+}
|
||||
+
|
||||
+const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"
|
||||
|
||||
-var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 155, 170, 184, 192, 205, 218, 231, 244, 255, 271, 286, 296}
|
||||
+var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 204, 217, 230, 243, 256, 267, 283, 298, 308, 317}
|
||||
|
||||
func (i state) String() string {
|
||||
if i >= state(len(_state_index)-1) {
|
||||
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
|
||||
index 06df679..92eb351 100644
|
||||
--- a/src/html/template/transition.go
|
||||
@@ -32,11 +32,11 @@ CVE: CVE-2023-39318
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
src/html/template/context.go | 6 ++-
|
||||
src/html/template/escape.go | 5 +-
|
||||
src/html/template/escape_test.go | 10 ++++
|
||||
src/html/template/state_string.go | 4 +-
|
||||
src/html/template/transition.go | 80 ++++++++++++++++++++-----------
|
||||
5 files changed, 72 insertions(+), 33 deletions(-)
|
||||
src/html/template/escape.go | 5 ++-
|
||||
src/html/template/escape_test.go | 10 +++++
|
||||
src/html/template/state_string.go | 26 +++++++------
|
||||
src/html/template/transition.go | 80 +++++++++++++++++++++++++--------------
|
||||
5 files changed, 84 insertions(+), 43 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/context.go b/src/html/template/context.go
|
||||
index f5f44a1..feb6517 100644
|
||||
@@ -105,14 +105,38 @@ diff --git a/src/html/template/state_string.go b/src/html/template/state_string.
|
||||
index 05104be..b5cfe70 100644
|
||||
--- a/src/html/template/state_string.go
|
||||
+++ b/src/html/template/state_string.go
|
||||
@@ -4,9 +4,9 @@ package template
|
||||
@@ -25,21 +25,23 @@ func _() {
|
||||
_ = x[stateJSRegexp-14]
|
||||
_ = x[stateJSBlockCmt-15]
|
||||
_ = x[stateJSLineCmt-16]
|
||||
- _ = x[stateCSS-17]
|
||||
- _ = x[stateCSSDqStr-18]
|
||||
- _ = x[stateCSSSqStr-19]
|
||||
- _ = x[stateCSSDqURL-20]
|
||||
- _ = x[stateCSSSqURL-21]
|
||||
- _ = x[stateCSSURL-22]
|
||||
- _ = x[stateCSSBlockCmt-23]
|
||||
- _ = x[stateCSSLineCmt-24]
|
||||
- _ = x[stateError-25]
|
||||
- _ = x[stateDead-26]
|
||||
+ _ = x[stateJSHTMLOpenCmt-17]
|
||||
+ _ = x[stateJSHTMLCloseCmt-18]
|
||||
+ _ = x[stateCSS-19]
|
||||
+ _ = x[stateCSSDqStr-20]
|
||||
+ _ = x[stateCSSSqStr-21]
|
||||
+ _ = x[stateCSSDqURL-22]
|
||||
+ _ = x[stateCSSSqURL-23]
|
||||
+ _ = x[stateCSSURL-24]
|
||||
+ _ = x[stateCSSBlockCmt-25]
|
||||
+ _ = x[stateCSSLineCmt-26]
|
||||
+ _ = x[stateError-27]
|
||||
+ _ = x[stateDead-28]
|
||||
}
|
||||
|
||||
import "strconv"
|
||||
|
||||
-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError"
|
||||
-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"
|
||||
+const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateJSHTMLOpenCmtstateJSHTMLCloseCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"
|
||||
|
||||
-var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 155, 170, 184, 192, 205, 218, 231, 244, 255, 271, 286, 296}
|
||||
-var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 204, 217, 230, 243, 256, 267, 283, 298, 308, 317}
|
||||
+var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 214, 233, 241, 254, 267, 280, 293, 304, 320, 335, 345, 354}
|
||||
|
||||
func (i state) String() string {
|
||||
|
||||
Reference in New Issue
Block a user