Merge pull request #1831 from a7i/amir/CVE-2024-44337

chore: upgrade github.com/gomarkdown/markdown to latest version
This commit is contained in:
Kubernetes Prow Robot
2026-02-19 09:37:37 +05:30
committed by GitHub
8 changed files with 227 additions and 97 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/glog v1.2.5 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 // indirect
github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.26.0 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
+2 -2
View File
@@ -130,8 +130,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0 h1:4gjrh/PN2MuWCCElk8/I4OCKRKWCCo2zEct3VKCbibU=
github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab h1:VYNivV7P8IRHUam2swVUNkhIdp0LRRFKe4hXNnoZKTc=
github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
+28 -19
View File
@@ -641,14 +641,29 @@ func (r *Renderer) EnsureUniqueHeadingID(id string) string {
return id
}
func (r *Renderer) headingEnter(w io.Writer, nodeData *ast.Heading) {
func (r *Renderer) MakeUniqueHeadingID(hdr *ast.Heading) string {
if hdr.HeadingID == "" {
return ""
}
id := r.EnsureUniqueHeadingID(hdr.HeadingID)
if r.Opts.HeadingIDPrefix != "" {
id = r.Opts.HeadingIDPrefix + id
}
if r.Opts.HeadingIDSuffix != "" {
id = id + r.Opts.HeadingIDSuffix
}
hdr.HeadingID = id
return id
}
func (r *Renderer) HeadingEnter(w io.Writer, hdr *ast.Heading) {
var attrs []string
var class string
// TODO(miek): add helper functions for coalescing these classes.
if nodeData.IsTitleblock {
if hdr.IsTitleblock {
class = "title"
}
if nodeData.IsSpecial {
if hdr.IsSpecial {
if class != "" {
class += " special"
} else {
@@ -659,35 +674,29 @@ func (r *Renderer) headingEnter(w io.Writer, nodeData *ast.Heading) {
attrs = []string{`class="` + class + `"`}
}
if nodeData.HeadingID != "" {
id := r.EnsureUniqueHeadingID(nodeData.HeadingID)
if r.Opts.HeadingIDPrefix != "" {
id = r.Opts.HeadingIDPrefix + id
}
if r.Opts.HeadingIDSuffix != "" {
id = id + r.Opts.HeadingIDSuffix
}
if hdr.HeadingID != "" {
id := r.MakeUniqueHeadingID(hdr)
attrID := `id="` + id + `"`
attrs = append(attrs, attrID)
}
attrs = append(attrs, BlockAttrs(nodeData)...)
attrs = append(attrs, BlockAttrs(hdr)...)
r.CR(w)
r.OutTag(w, HeadingOpenTagFromLevel(nodeData.Level), attrs)
r.OutTag(w, HeadingOpenTagFromLevel(hdr.Level), attrs)
}
func (r *Renderer) headingExit(w io.Writer, heading *ast.Heading) {
r.Outs(w, HeadingCloseTagFromLevel(heading.Level))
if !(IsListItem(heading.Parent) && ast.GetNextNode(heading) == nil) {
func (r *Renderer) HeadingExit(w io.Writer, hdr *ast.Heading) {
r.Outs(w, HeadingCloseTagFromLevel(hdr.Level))
if !(IsListItem(hdr.Parent) && ast.GetNextNode(hdr) == nil) {
r.CR(w)
}
}
// Heading writes ast.Heading node
func (r *Renderer) Heading(w io.Writer, node *ast.Heading, entering bool) {
func (r *Renderer) Heading(w io.Writer, hdr *ast.Heading, entering bool) {
if entering {
r.headingEnter(w, node)
r.HeadingEnter(w, hdr)
} else {
r.headingExit(w, node)
r.HeadingExit(w, hdr)
}
}
+84 -40
View File
@@ -76,6 +76,7 @@ var (
"output": {},
"progress": {},
"section": {},
"svg": {},
"video": {},
}
)
@@ -925,7 +926,7 @@ func syntaxRange(data []byte, iout *int) (int, int) {
i++
} else {
for i < n && !IsSpace(data[i]) {
for i < n && data[i] != '\n' {
syn++
i++
}
@@ -950,8 +951,6 @@ func (p *Parser) fencedCodeBlock(data []byte, doRender bool) int {
work.WriteByte('\n')
for {
// safe to assume beg < len(data)
// check for the end of the code block
fenceEnd, _ := isFenceLine(data[beg:], nil, marker)
if fenceEnd != 0 {
@@ -968,48 +967,47 @@ func (p *Parser) fencedCodeBlock(data []byte, doRender bool) int {
}
// verbatim copy to the working buffer
if doRender {
work.Write(data[beg:end])
}
work.Write(data[beg:end])
beg = end
}
if doRender {
codeBlock := &ast.CodeBlock{
IsFenced: true,
}
codeBlock.Content = work.Bytes() // TODO: get rid of temp buffer
if !doRender {
return beg
}
codeBlock := &ast.CodeBlock{
IsFenced: true,
}
codeBlock.Content = work.Bytes() // TODO: get rid of temp buffer
if p.extensions&Mmark == 0 {
p.AddBlock(codeBlock)
finalizeCodeBlock(codeBlock)
return beg
}
// Check for caption and if found make it a figure.
if captionContent, id, consumed := p.caption(data[beg:], []byte(captionFigure)); consumed > 0 {
figure := &ast.CaptionFigure{}
caption := &ast.Caption{}
figure.HeadingID = id
p.Inline(caption, captionContent)
p.AddBlock(figure)
codeBlock.AsLeaf().Attribute = figure.AsContainer().Attribute
p.addChild(codeBlock)
finalizeCodeBlock(codeBlock)
p.addChild(caption)
p.Finalize(figure)
beg += consumed
return beg
}
// Still here, normal block
if p.extensions&Mmark == 0 {
p.AddBlock(codeBlock)
finalizeCodeBlock(codeBlock)
return beg
}
// Check for caption and if found make it a figure.
if captionContent, id, consumed := p.caption(data[beg:], []byte(captionFigure)); consumed > 0 {
figure := &ast.CaptionFigure{}
caption := &ast.Caption{}
figure.HeadingID = id
p.Inline(caption, captionContent)
p.AddBlock(figure)
codeBlock.AsLeaf().Attribute = figure.AsContainer().Attribute
p.addChild(codeBlock)
finalizeCodeBlock(codeBlock)
p.addChild(caption)
p.Finalize(figure)
beg += consumed
return beg
}
// Still here, normal block
p.AddBlock(codeBlock)
finalizeCodeBlock(codeBlock)
return beg
}
@@ -1352,6 +1350,7 @@ func finalizeList(list *ast.List) {
// Parse a single list item.
// Assumes initial prefix is already removed if this is a sublist.
func (p *Parser) listItem(data []byte, flags *ast.ListType) int {
isDefinitionList := *flags&ast.ListTypeDefinition != 0
// keep track of the indentation of the first line
itemIndent := 0
if data[0] == '\t' {
@@ -1384,7 +1383,7 @@ func (p *Parser) listItem(data []byte, flags *ast.ListType) int {
}
if i == 0 {
// if in definition list, set term flag and continue
if *flags&ast.ListTypeDefinition != 0 {
if isDefinitionList {
*flags |= ast.ListTypeTerm
} else {
return 0
@@ -1410,6 +1409,9 @@ func (p *Parser) listItem(data []byte, flags *ast.ListType) int {
// process the following lines
containsBlankLine := false
sublist := 0
// track fenced code blocks inside list items so that lines within
// the fence are gathered verbatim (not misinterpreted as list items)
fenceMarker := ""
gatherlines:
for line < len(data) {
@@ -1443,9 +1445,49 @@ gatherlines:
chunk := data[line+indentIndex : i]
// track fenced code blocks inside list items;
// only track fences that are indented (part of the list item content),
// a fence at indent 0 ends the list (handled below)
if !isDefinitionList && p.extensions&FencedCode != 0 {
if fenceMarker != "" {
if indent == 0 {
// non-indented line while inside a fence means we
// left the list item content -- abandon the fence
fenceMarker = ""
} else {
// inside a fence: check for closing fence
_, marker := isFenceLine(chunk, nil, fenceMarker)
if marker != "" {
fenceMarker = ""
}
// gather the line verbatim, skip structure detection
if containsBlankLine {
containsBlankLine = false
raw.WriteByte('\n')
}
raw.Write(chunk)
line = i
continue
}
} else if indent > 0 {
// not inside a fence: check for opening fence (indented only)
_, marker := isFenceLine(chunk, nil, "")
if marker != "" {
fenceMarker = marker
}
}
}
// If there is a fence line (marking starting of a code block)
// without indent do not process it as part of the list.
if p.extensions&FencedCode != 0 {
//
// does not apply for definition lists because it causes infinite
// loop if text before defintion term is fenced code block start
// marker but not part of actual fenced code block
// for defnition lists we're called after parsing fence code blocks
// so we kno this cannot be a fenced block
// https://github.com/gomarkdown/markdown/issues/326
if !isDefinitionList && p.extensions&FencedCode != 0 {
fenceLineEnd, _ := isFenceLine(chunk, nil, "")
if fenceLineEnd > 0 && indent == 0 {
*flags |= ast.ListItemEndOfList
@@ -1652,7 +1694,9 @@ func (p *Parser) paragraph(data []byte) int {
if p.extensions&DefinitionLists != 0 {
if i < len(data)-1 && data[i+1] == ':' {
listLen := p.list(data[prev:], ast.ListTypeDefinition, 0, '.')
return prev + listLen
if listLen > 0 {
return prev + listLen
}
}
}
+3
View File
@@ -49,6 +49,9 @@ func citation(p *Parser, data []byte, offset int) (int, ast.Node) {
for _, citation := range citations {
var suffix []byte
citation = bytes.TrimSpace(citation)
if len(citation) == 0 {
continue
}
j := 0
if citation[j] != '@' {
// not a citation, drop out entirely.
+76 -27
View File
@@ -271,7 +271,7 @@ func maybeInlineFootnoteOrSuper(p *Parser, data []byte, offset int) (int, ast.No
// '[': parse a link or an image or a footnote or a citation
func link(p *Parser, data []byte, offset int) (int, ast.Node) {
// no links allowed inside regular links, footnote, and deferred footnotes
if p.insideLink && (offset > 0 && data[offset-1] == '[' || len(data)-1 > offset && data[offset+1] == '^') {
if p.InsideLink && (offset > 0 && data[offset-1] == '[' || len(data)-1 > offset && data[offset+1] == '^') {
return 0, nil
}
@@ -362,25 +362,27 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
linkB := i
brace := 0
var c byte
// look for link end: ' " )
findlinkend:
for i < len(data) {
c = data[i]
switch {
case data[i] == '\\':
case c == '\\':
i += 2
case data[i] == '(':
case c == '(':
brace++
i++
case data[i] == ')':
case c == ')':
if brace <= 0 {
break findlinkend
}
brace--
i++
case data[i] == '\'' || data[i] == '"':
case c == '\'' || c == '"':
break findlinkend
default:
@@ -402,14 +404,15 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
findtitleend:
for i < len(data) {
c = data[i]
switch {
case data[i] == '\\':
case c == '\\':
i++
case data[i] == data[titleB-1]: // matching title delimiter
case c == data[titleB-1]: // matching title delimiter
titleEndCharFound = true
case titleEndCharFound && data[i] == ')':
case titleEndCharFound && c == ')':
break findtitleend
}
i++
@@ -619,10 +622,10 @@ func link(p *Parser, data []byte, offset int) (int, ast.Node) {
} else {
// links cannot contain other links, so turn off link parsing
// temporarily and recurse
insideLink := p.insideLink
p.insideLink = true
InsideLink := p.InsideLink
p.InsideLink = true
p.Inline(link, data[1:txtE])
p.insideLink = insideLink
p.InsideLink = InsideLink
}
return i, link
@@ -736,7 +739,7 @@ func leftAngle(p *Parser, data []byte, offset int) (int, ast.Node) {
}
// '\\' backslash escape
var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~^$")
var EscapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~^$")
func escape(p *Parser, data []byte, offset int) (int, ast.Node) {
data = data[offset:]
@@ -753,7 +756,7 @@ func escape(p *Parser, data []byte, offset int) (int, ast.Node) {
return 2, &ast.Hardbreak{}
}
if bytes.IndexByte(escapeChars, data[1]) < 0 {
if bytes.IndexByte(EscapeChars, data[1]) < 0 {
return 0, nil
}
@@ -814,7 +817,12 @@ func entity(p *Parser, data []byte, offset int) (int, ast.Node) {
codepoint, err = strconv.ParseUint(string(ent[2:len(ent)-1]), 10, 64)
}
if err == nil { // only if conversion was valid return here.
return end, newTextNode([]byte(string(rune(codepoint))))
r := rune(codepoint)
// Replace invalid codepoints with U+FFFD per CommonMark spec section 6.2
if r == 0 || (r >= 0xD800 && r <= 0xDFFF) || r > 0x10FFFF {
r = '\uFFFD'
}
return end, newTextNode([]byte(string(r)))
}
return end, newTextNode(ent)
@@ -857,7 +865,7 @@ const shortestPrefix = 6 // len("ftp://"), the shortest of the above
func maybeAutoLink(p *Parser, data []byte, offset int) (int, ast.Node) {
// quick check to rule out most false hits
if p.insideLink || len(data) < offset+shortestPrefix {
if p.InsideLink || len(data) < offset+shortestPrefix {
return 0, nil
}
for _, prefix := range protocolPrefixes {
@@ -1070,10 +1078,11 @@ func tagLength(data []byte) (autolink autolinkType, end int) {
// one of the forbidden chars has been found
autolink = notAutolink
}
i += bytes.IndexByte(data[i:], '>')
if i < 0 {
j = bytes.IndexByte(data[i:], '>')
if j < 0 {
return autolink, 0
}
i += j
return autolink, i + 1
}
@@ -1185,30 +1194,30 @@ func helperFindEmphChar(data []byte, c byte) int {
func helperEmphasis(p *Parser, data []byte, c byte) (int, ast.Node) {
i := 0
// skip one symbol if coming from emph3
// skip two symbol if coming from emph3, as it detected a double emphasis case
if len(data) > 1 && data[0] == c && data[1] == c {
i = 1
i = 2
}
for i < len(data) {
length := helperFindEmphChar(data[i:], c)
if length == 0 {
return 0, nil
}
i += length
if i >= len(data) {
return 0, nil
}
if i+1 < len(data) && data[i+1] == c {
i++
i += 2
continue
}
if data[i] == c && !IsSpace(data[i-1]) {
if p.extensions&NoIntraEmphasis != 0 {
if !(i+1 == len(data) || IsSpace(data[i+1]) || IsPunctuation(data[i+1])) {
rest := data[i+1:]
if !(len(rest) == 0 || IsSpace(rest[0]) || IsPunctuation2(rest)) {
if length == 0 {
return 0, nil
}
continue
}
}
@@ -1217,6 +1226,11 @@ func helperEmphasis(p *Parser, data []byte, c byte) (int, ast.Node) {
p.Inline(emph, data[:i])
return i + 1, emph
}
// We have to check this at the end, otherwise the scenario where we find repeated c's will get skipped
if length == 0 {
return 0, nil
}
}
return 0, nil
@@ -1233,18 +1247,53 @@ func helperDoubleEmphasis(p *Parser, data []byte, c byte) (int, ast.Node) {
i += length
if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !IsSpace(data[i-1]) {
// When the closing delimiter is *** (3+ chars) and there is an
// unclosed single emphasis opener inside the content, include
// one extra char in the content so that the inner emphasis can
// pair with it. For example: **bold *ital*** should produce
// <strong>bold <em>ital</em></strong>, not <strong>bold *ital</strong>*.
// See https://github.com/gomarkdown/markdown/issues/279
contentEnd := i
if i+2 < len(data) && data[i+2] == c && c != '~' {
if hasTrailingEmphOpener(data[:i], c) {
contentEnd = i + 1
}
}
var node ast.Node = &ast.Strong{}
if c == '~' {
node = &ast.Del{}
}
p.Inline(node, data[:i])
return i + 2, node
p.Inline(node, data[:contentEnd])
return contentEnd + 2, node
}
i++
}
return 0, nil
}
// hasTrailingEmphOpener checks if the last occurrence of c in data is an
// unclosed opener. An opener is c preceded by whitespace or start of data,
// followed by non-whitespace. If the last c is a closer (preceded by
// non-whitespace), the emphasis pair is balanced and we should not shift
// the content boundary.
func hasTrailingEmphOpener(data []byte, c byte) bool {
// find the last c in data
last := -1
for j := len(data) - 1; j >= 0; j-- {
if data[j] == c {
last = j
break
}
}
if last < 0 {
return false
}
// opener: preceded by space/start, followed by non-space
return (last == 0 || IsSpace(data[last-1])) &&
last+1 < len(data) && !IsSpace(data[last+1])
}
func helperTripleEmphasis(p *Parser, data []byte, offset int, c byte) (int, ast.Node) {
i := 0
origData := data
+32 -7
View File
@@ -8,6 +8,8 @@ import (
"fmt"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/gomarkdown/markdown/ast"
)
@@ -56,7 +58,7 @@ const (
)
// for each character that triggers a response when parsing inline data.
type inlineParser func(p *Parser, data []byte, offset int) (int, ast.Node)
type InlineParser func(p *Parser, data []byte, offset int) (int, ast.Node)
// ReferenceOverrideFunc is expected to be called with a reference string and
// return either a valid Reference type that the reference string maps to or
@@ -98,10 +100,10 @@ type Parser struct {
refs map[string]*reference
refsRecord map[string]struct{}
inlineCallback [256]inlineParser
inlineCallback [256]InlineParser
nesting int
maxNesting int
insideLink bool
InsideLink bool
indexCnt int // incremented after every index
// Footnotes need to be ordered as well as available to quickly check for
@@ -122,6 +124,8 @@ type Parser struct {
// collect headings where we auto-generated id so that we can
// ensure they are unique at the end
allHeadingsWithAutoID []*ast.Heading
didParse bool
}
// New creates a markdown parser with CommonExtensions.
@@ -138,8 +142,8 @@ func NewWithExtensions(extension Extensions) *Parser {
p := Parser{
refs: make(map[string]*reference),
refsRecord: make(map[string]struct{}),
maxNesting: 16,
insideLink: false,
maxNesting: 64,
InsideLink: false,
Doc: &ast.Document{},
extensions: extension,
allClosed: true,
@@ -181,7 +185,7 @@ func NewWithExtensions(extension Extensions) *Parser {
return &p
}
func (p *Parser) RegisterInline(n byte, fn inlineParser) inlineParser {
func (p *Parser) RegisterInline(n byte, fn InlineParser) InlineParser {
prev := p.inlineCallback[n]
p.inlineCallback[n] = fn
return prev
@@ -290,7 +294,14 @@ type Reference struct {
//
// You can then convert AST to html using html.Renderer, to some other format
// using a custom renderer or transform the tree.
//
// Parser is not reusable. Create a new Parser for each Parse() call.
func (p *Parser) Parse(input []byte) ast.Node {
if p.didParse {
panic("Parser is not reusable. Must create new Parser for each Parse() call.")
}
p.didParse = true
// the code only works with Unix CR newlines so to make life easy for
// callers normalize newlines
input = NormalizeNewlines(input)
@@ -727,7 +738,21 @@ func IsPunctuation(c byte) bool {
return false
}
// IsSpace returns true if c is a white-space charactr
func IsPunctuation2(d []byte) bool {
if len(d) == 0 {
return false
}
if IsPunctuation(d[0]) {
return true
}
r, _ := utf8.DecodeRune(d)
if r == utf8.RuneError {
return false
}
return unicode.IsPunct(r)
}
// IsSpace returns true if c is a white-space character
func IsSpace(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'
}
+1 -1
View File
@@ -103,7 +103,7 @@ github.com/golang/glog/internal/stackdump
# github.com/golang/protobuf v1.5.4
## explicit; go 1.17
github.com/golang/protobuf/proto
# github.com/gomarkdown/markdown v0.0.0-20240328165702-4d01890c35c0
# github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab
## explicit; go 1.12
github.com/gomarkdown/markdown/ast
github.com/gomarkdown/markdown/html