build(deps): bump github.com/beevik/etree from 1.6.0 to 1.7.0

Bumps [github.com/beevik/etree](https://github.com/beevik/etree) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/beevik/etree/releases)
- [Changelog](https://github.com/beevik/etree/blob/main/RELEASE_NOTES.md)
- [Commits](https://github.com/beevik/etree/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/beevik/etree
  dependency-version: 1.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2026-07-14 14:43:33 +00:00
committed by GitHub
parent fcde95750b
commit 19f6e600f2
7 changed files with 187 additions and 23 deletions

2
go.mod
View File

@@ -10,7 +10,7 @@ require (
github.com/MicahParks/keyfunc/v2 v2.1.0
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/bbalet/stopwords v1.0.0
github.com/beevik/etree v1.6.0
github.com/beevik/etree v1.7.0
github.com/blevesearch/bleve/v2 v2.6.0
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.20.0

4
go.sum
View File

@@ -132,8 +132,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo=
github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc=
github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=
github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
github.com/beevik/etree v1.7.0 h1:xjBk9O4p4x7D1YajePjfLzdaFC4/uYUENA7P0pv6gXA=
github.com/beevik/etree v1.7.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=

View File

@@ -1,3 +1,27 @@
Release 1.7.0
=============
**Changes**
**Breaking changes**
* To address a security issue, it was necessary to add a `MaxDepth` option to
`ReadSettings` to limit the depth of XML trees during parsing. A generous
default value of 1024 was chosen to avoid breaking most existing code.
However, if your code is processing XML hierarchies with a depth greater
than 1024, you will need to assign your `Document` a `ReadSettings` that has
a `MaxDepth` set to a higher value.
**Security Fixes**
* Limited the depth of XML trees processed by all `ReadFrom` functions during
parsing.
* Fixed a `CompilePath` index-out-of-range panic that could be caused by a
missing path filter key.
* Sanitized the contents of XML text, comment, ProcInst and Directive tokens
provided by the user.
Release 1.6.0
=============

View File

@@ -13,6 +13,7 @@ import (
"errors"
"io"
"iter"
"maps"
"os"
"slices"
"strings"
@@ -27,6 +28,10 @@ const (
// ErrXML is returned when XML parsing fails due to incorrect formatting.
var ErrXML = errors.New("etree: invalid XML format")
// ErrMaxDepth is returned when the depth of the XML tree being read exceeds
// the maximum depth allowed by ReadSettings.MaxDepth.
var ErrMaxDepth = errors.New("etree: XML tree exceeds maximum depth")
// cdataPrefix is used to detect CDATA text when ReadSettings.PreserveCData is
// true.
var cdataPrefix = []byte("<![CDATA[")
@@ -76,8 +81,18 @@ type ReadSettings struct {
// whether an end element is present. Commonly set to xml.HTMLAutoClose.
// Default: nil.
AutoClose []string
// MaxDepth is the maximum depth of the XML tree to parse. If the depth of
// the XML tree exceeds this value, all ReadFrom* functions return the
// error ErrMaxDepth. If MaxDepth is zero or negative, a depth limit of
// 1024 is used. Default: 0 (i.e., a limit of 1024).
MaxDepth int
}
// defaultMaxDepth is the maximum depth of an XML tree parsed by ReadFrom*
// functions when ReadSettings.MaxDepth is not set to a positive value.
const defaultMaxDepth = 1024
// defaultCharsetReader is used by the xml decoder when the ReadSettings
// CharsetReader value is nil. It behaves as a "pass-through", ignoring
// the requested charset parameter and skipping conversion altogether.
@@ -87,18 +102,9 @@ func defaultCharsetReader(charset string, input io.Reader) (io.Reader, error) {
// dup creates a duplicate of the ReadSettings object.
func (s *ReadSettings) dup() ReadSettings {
var entityCopy map[string]string
if s.Entity != nil {
entityCopy = make(map[string]string)
for k, v := range s.Entity {
entityCopy[k] = v
}
}
return ReadSettings{
CharsetReader: s.CharsetReader,
Permissive: s.Permissive,
Entity: entityCopy,
}
c := *s
c.Entity = maps.Clone(s.Entity)
return c
}
// WriteSettings determine the behavior of the Document's WriteTo* functions.
@@ -913,6 +919,11 @@ func (e *Element) readFrom(ri io.Reader, settings ReadSettings) (n int64, err er
attrCheck := make(map[xml.Name]int)
dec := newDecoder(r, settings)
maxDepth := settings.MaxDepth
if maxDepth <= 0 {
maxDepth = defaultMaxDepth
}
var stack stack[*Element]
stack.push(e)
for {
@@ -942,6 +953,9 @@ func (e *Element) readFrom(ri io.Reader, settings ReadSettings) (n int64, err er
switch t := t.(type) {
case xml.StartElement:
if len(stack.data) > maxDepth {
return r.Bytes(), ErrMaxDepth
}
e := newElement(t.Name.Space, t.Name.Local, top)
if settings.PreserveDuplicateAttrs || len(t.Attr) < 2 {
for _, a := range t.Attr {
@@ -1622,7 +1636,7 @@ func (c *CharData) Index() int {
func (c *CharData) WriteTo(w Writer, s *WriteSettings) {
if c.IsCData() {
w.WriteString(`<![CDATA[`)
w.WriteString(c.Data)
sanitizeCData(w, c.Data)
w.WriteString(`]]>`)
} else {
var m escapeMode
@@ -1704,7 +1718,7 @@ func (c *Comment) Index() int {
// WriteTo serialies the comment to the writer.
func (c *Comment) WriteTo(w Writer, s *WriteSettings) {
w.WriteString("<!--")
w.WriteString(c.Data)
sanitizeComment(w, c.Data)
w.WriteString("-->")
}
@@ -1769,7 +1783,7 @@ func (d *Directive) Index() int {
// WriteTo serializes the XML directive to the writer.
func (d *Directive) WriteTo(w Writer, s *WriteSettings) {
w.WriteString("<!")
w.WriteString(d.Data)
sanitizeDirective(w, d.Data)
w.WriteString(">")
}
@@ -1837,10 +1851,10 @@ func (p *ProcInst) Index() int {
// WriteTo serializes the processing instruction to the writer.
func (p *ProcInst) WriteTo(w Writer, s *WriteSettings) {
w.WriteString("<?")
w.WriteString(p.Target)
sanitizeProcInst(w, p.Target)
if p.Inst != "" {
w.WriteByte(' ')
w.WriteString(p.Inst)
sanitizeProcInst(w, p.Inst)
}
w.WriteString("?>")
}

View File

@@ -384,6 +384,120 @@ func escapeString(w Writer, s string, m escapeMode) {
w.WriteString(s[last:])
}
// sanitizeCData writes the sanitized contents of a CDATA section to the
// writer. XML provides no way to escape the "]]>" sequence within a CDATA
// section, so any occurrence of it is split across two CDATA sections.
func sanitizeCData(w Writer, s string) {
for {
i := strings.Index(s, "]]>")
if i < 0 {
break
}
w.WriteString(s[:i+2])
w.WriteString("]]><![CDATA[")
s = s[i+2:]
}
w.WriteString(s)
}
// sanitizeComment writes the sanitized contents of a comment to the writer.
// An XML comment may not contain the string "--", and it may not end with a
// '-'. Because XML provides no way to escape these sequences, spaces are
// inserted where necessary.
func sanitizeComment(w Writer, s string) {
last, hyphen := 0, false
for i := 0; i < len(s); i++ {
if s[i] != '-' {
hyphen = false
continue
}
if hyphen {
w.WriteString(s[last:i])
w.WriteByte(' ')
last = i
}
hyphen = true
}
w.WriteString(s[last:])
if hyphen {
w.WriteByte(' ')
}
}
// sanitizeProcInst writes the contents of a sanitized processing instruction
// to the writer. XML provides no way to escape the "?>" sequence within a
// processing instruction, so a space is inserted between the two characters.
func sanitizeProcInst(w Writer, s string) {
for {
i := strings.Index(s, "?>")
if i < 0 {
break
}
w.WriteString(s[:i+1])
w.WriteByte(' ')
s = s[i+1:]
}
w.WriteString(s)
}
// sanitizeDirective writes the sanitized contents of an XML directive to the
// writer.
func sanitizeDirective(w Writer, s string) {
// The XML decoder reserves the character following "<!" for comments
// ('-') and CDATA sections ('['), and it treats "<!>" as an unterminated
// directive. Insert a space to avoid conflicts with reserved sequences.
scan := s
if s == "" || s[0] == '-' || s[0] == '[' {
w.WriteByte(' ')
} else {
scan = s[1:]
}
// A directive's contents may legitimately contain '<' and '>' characters,
// so write them without modification when they are balanced.
if isDirectiveBalanced(scan) {
w.WriteString(s)
return
}
// The contents are unbalanced, so escape every character in the string.
escapeString(w, s, escapeNormal)
}
// isDirectiveBalanced returns true if the interpreted portion of an XML
// directive's contents may be enclosed by "<!" and ">" without changing the
// extents of the resulting directive.
func isDirectiveBalanced(s string) bool {
var quote byte
var depth int
for i := 0; i < len(s); i++ {
switch c := s[i]; {
case quote != 0:
if c == quote {
quote = 0
}
case c == '\'' || c == '"':
quote = c
case c == '>':
if depth == 0 {
return false
}
depth--
case c == '<':
if !strings.HasPrefix(s[i+1:], "!--") {
depth++
break
}
j := strings.Index(s[i+4:], "-->")
if j < 0 {
return false
}
i += 4 + j + 2
}
}
return quote == 0 && depth == 0
}
func isInCharacterRange(r rune) bool {
return r == 0x09 ||
r == 0x0A ||

View File

@@ -281,7 +281,11 @@ func (c *compiler) parseSegment(path string) segment {
c.err = ErrPath("path has invalid filter [brackets].")
break
}
seg.filters = append(seg.filters, c.parseFilter(fpath[:len(fpath)-1]))
filter := c.parseFilter(fpath[:len(fpath)-1])
if c.err != ErrPath("") {
break
}
seg.filters = append(seg.filters, filter)
}
return seg
}
@@ -320,7 +324,11 @@ func (c *compiler) parseFilter(path string) filter {
// Filter contains [@attr='val'], [@attr="val"], [fn()='val'],
// [fn()="val"], [tag='val'] or [tag="val"]?
eqindex := strings.IndexByte(path, '=')
if eqindex >= 0 && eqindex+1 < len(path) {
if eqindex == 0 {
c.err = ErrPath("path contains a filter expression with no key.")
return nil
}
if eqindex > 0 && eqindex+1 < len(path) {
quote := path[eqindex+1]
if quote == '\'' || quote == '"' {
rindex := nextIndex(path, quote, eqindex+2)
@@ -334,6 +342,10 @@ func (c *compiler) parseFilter(path string) filter {
switch {
case key[0] == '@':
if len(key) == 1 {
c.err = ErrPath("path contains a filter expression with no key.")
return nil
}
return newFilterAttrVal(key[1:], value)
case strings.HasSuffix(key, "()"):
name := key[:len(key)-2]

2
vendor/modules.txt vendored
View File

@@ -106,7 +106,7 @@ github.com/asaskevich/govalidator
# github.com/bbalet/stopwords v1.0.0
## explicit
github.com/bbalet/stopwords
# github.com/beevik/etree v1.6.0
# github.com/beevik/etree v1.7.0
## explicit; go 1.23.0
github.com/beevik/etree
# github.com/beorn7/perks v1.0.1