1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-12-23 22:11:10 +02:00
Files
imgproxy/xmlparser/entities.go
2025-11-15 21:27:42 +03:00

124 lines
2.7 KiB
Go

package xmlparser
import (
"bytes"
"regexp"
"strings"
)
var entityDeclRe = regexp.MustCompile(`<!ENTITY\s+(\S+)\s+("[^"]+"|'[^']+')\s*>`)
var entityCommentRe = regexp.MustCompile(`(?s)<!--.*?-->`)
// parseEntityMap searches for a DOCTYPE directive in the given nodes
// and extracts entity declarations into a map.
func parseEntityMap(nodes []any) map[string][]byte {
// Find doctype
var doctype *Directive
for _, node := range nodes {
if dir, ok := node.(*Directive); ok {
doctype = dir
break
}
}
if doctype == nil {
return nil
}
start := bytes.IndexByte(doctype.Data, '[')
if start == -1 {
return nil
}
data := doctype.Data[start:]
// Remove comments to avoid false positives
if bytes.Contains(data, commentStart) {
data = entityCommentRe.ReplaceAll(data, []byte{})
}
matches := entityDeclRe.FindAllSubmatch(data, -1)
if matches == nil {
return nil
}
em := make(map[string][]byte, len(matches))
for _, match := range matches {
name := match[1]
value := bytes.Trim(match[2], `"'`)
em[string(name)] = value
}
return em
}
// replaceEntitiesBytes replaces XML entities in the given byte slice
// according to the provided entity map.
func replaceEntitiesBytes(data []byte, em map[string][]byte) []byte {
return replaceEntities(data, em, bytes.IndexByte)
}
// replaceEntitiesString replaces XML entities in the given string
// according to the provided entity map.
func replaceEntitiesString(data string, em map[string][]byte) string {
return replaceEntities(data, em, strings.IndexByte)
}
func replaceEntities[T string | []byte](
data T,
em map[string][]byte,
indexFn func(T, byte) int,
) T {
var replaced []byte
searchInd := 0
for {
// Find the entity start
i := indexFn(data[searchInd:], '&')
if i == -1 {
break
}
i += searchInd
// Find the entity end
j := indexFn(data[i:], ';')
if j == -1 {
break
}
j += i
// Get the entity name
name := data[i+1 : j]
// Find the replacement value
if val, ok := em[string(name)]; ok {
// If this is the first replacement, prealloc the replaced slice
if len(replaced) == 0 {
replaced = make([]byte, 0, len(data))
}
// Append the data before the entity and the replacement value
replaced = append(replaced, data[:i]...)
replaced = append(replaced, val...)
// Move the data pointer forward and reset search index
data = data[j+1:]
searchInd = 0
} else {
// Didn't find replacement, just move the search index forward
searchInd = j + 1
}
}
// If no replacements were made, return the original data
if len(replaced) == 0 {
return data
}
// Append any remaining data after the last entity
replaced = append(replaced, data...)
return T(replaced)
}