Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions block.go
Original file line number Diff line number Diff line change
Expand Up @@ -1066,10 +1066,10 @@ func (p *parser) dliPrefix(data []byte) int {
i := 0

// need a : followed by a spaces
if data[i] != ':' || data[i+1] != ' ' {
if len(data) < 2 || data[i] != ':' || data[i+1] != ' ' {
return 0
}
for data[i] == ' ' {
for i < len(data) && data[i] == ' ' {
i++
}
return i + 2
Expand Down Expand Up @@ -1126,13 +1126,13 @@ func (p *parser) listItem(out *bytes.Buffer, data []byte, flags *int) int {
}

// skip leading whitespace on first line
for data[i] == ' ' {
for i < len(data) && data[i] == ' ' {
i++
}

// find the end of the line
line := i
for i > 0 && data[i-1] != '\n' {
for i < len(data) && (i == 0 || data[i-1] != '\n') {
i++
}

Expand All @@ -1157,9 +1157,13 @@ gatherlines:
i++

// find the end of this line
for data[i-1] != '\n' {
for i < len(data) && data[i-1] != '\n' {
i++
}
// if we reached end of data without newline, treat as end of line
if i > len(data) {
i = len(data)
}
// if it is an empty line, guess that it is part of this item
// and move on to the next line
if p.isEmpty(data[line:i]) > 0 {
Expand Down Expand Up @@ -1246,13 +1250,13 @@ gatherlines:
if *flags&LIST_TYPE_DEFINITION != 0 && i < len(data)-1 {
// is the next item still a part of this list?
next := i
for data[next] != '\n' {
for next < len(data) && data[next] != '\n' {
next++
}
for next < len(data)-1 && data[next] == '\n' {
next++
}
if i < len(data)-1 && data[i] != ':' && data[next] != ':' {
if i < len(data)-1 && data[i] != ':' && next < len(data) && data[next] != ':' {
*flags |= LIST_ITEM_END_OF_LIST
}
} else {
Expand Down
69 changes: 69 additions & 0 deletions block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1854,3 +1854,72 @@ func TestSanitizedAnchorName(t *testing.T) {
}
}
}

// TestListItemBoundsCheck tests that list items without trailing newlines
// don't cause index out of range panics.
// See: https://github.com/russross/blackfriday/issues/xxx
func TestListItemBoundsCheck(t *testing.T) {
// These test cases should not panic
testCases := []struct {
name string
input string
}{
{
name: "unordered list without trailing newline",
input: "* item",
},
{
name: "ordered list without trailing newline",
input: "1. item",
},
{
name: "unordered list with spaces no newline",
input: "* item with spaces",
},
{
name: "ordered list with spaces no newline",
input: "1. item with spaces",
},
{
name: "nested list without trailing newline",
input: "* item\n * nested",
},
{
name: "list with multiple items no trailing newline",
input: "* item1\n* item2",
},
{
name: "definition list without trailing newline",
input: "Term\n: Definition",
},
{
name: "empty list marker",
input: "* ",
},
{
name: "list with only spaces after marker",
input: "* ",
},
}

defer func() {
if r := recover(); r != nil {
t.Errorf("listItem caused panic: %v", r)
}
}()

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Errorf("Test %q panicked: %v", tc.name, r)
}
}()
// Just ensure it doesn't panic - output correctness is secondary
_ = runMarkdownBlock(tc.input, 0)
_ = runMarkdownBlock(tc.input, EXTENSION_DEFINITION_LISTS)
_ = runMarkdownBlock(tc.input, EXTENSION_FENCED_CODE)
_ = runMarkdownBlock(tc.input, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK)
})
}
}