fix(sanitize): bound destination scans to eliminate quadratic sanitiz… · github/github-mcp-server@335eed9 · GitHub
Skip to content

Commit 335eed9

Browse files
fix(sanitize): bound destination scans to eliminate quadratic sanitization
Bound the forward scans in inlineDestinationSpan, referenceLinkDestinationSpan, and angleAutoLinkSpan to maxLinkDestinationLength so malformed/unterminated candidates fail fast instead of scanning to end-of-input on every call. Also reject a reference-link bare destination when parenthesis depth is nonzero at termination, so unbalanced destinations are left for hidden-content neutralization rather than incorrectly masked. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 69c5ab30-9815-4c07-8385-11a206e68f66
1 parent e1a31b8 commit 335eed9

3 files changed

Lines changed: 70 additions & 4 deletions

File tree

‎pkg/github/sanitize_coverage_test.go‎

Lines changed: 17 additions & 0 deletions

‎pkg/sanitize/sanitize.go‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ func Content(input string) string {
8585

8686
const maxContentFilterPasses = 4
8787

88+
// maxLinkDestinationLength bounds how far destination-scanning functions look
89+
// for a terminator (matching bracket, closing angle bracket) before giving
90+
// up. Without a bound, an unterminated destination forces a scan to the end
91+
// of input on every call site, which is quadratic across many such
92+
// candidates.
93+
const maxLinkDestinationLength = 4096
94+
8895
var markdownParser = goldmark.DefaultParser()
8996

9097
type sourceSpan struct {
@@ -548,7 +555,8 @@ func referenceLinkDestinationSpan(source []byte, start int) (sourceSpan, int, bo
548555

549556
parentheses := 0
550557
stop := start
551-
for stop < len(source) {
558+
limit := min(len(source), start+maxLinkDestinationLength)
559+
for stop < limit {
552560
r, size := utf8.DecodeRune(source[stop:])
553561
if unicode.IsSpace(r) {
554562
break
@@ -563,11 +571,15 @@ func referenceLinkDestinationSpan(source []byte, start int) (sourceSpan, int, bo
563571
}
564572
stop += size
565573
}
574+
if parentheses != 0 {
575+
return sourceSpan{}, start, false
576+
}
566577
return sourceSpan{start: start, stop: stop}, stop, true
567578
}
568579

569580
func angleAutoLinkSpan(source []byte, start int) (sourceSpan, int, bool) {
570-
closeOffset := bytes.IndexByte(source[start+1:], '>')
581+
searchLimit := min(len(source), start+1+maxLinkDestinationLength)
582+
closeOffset := bytes.IndexByte(source[start+1:searchLimit], '>')
571583
if closeOffset < 0 {
572584
return sourceSpan{}, start, false
573585
}
@@ -600,7 +612,8 @@ func inlineDestinationSpan(source []byte, start int) (sourceSpan, int, bool) {
600612
}
601613

602614
if source[start] == '<' {
603-
for stop := start + 1; stop < len(source); stop++ {
615+
limit := min(len(source), start+1+maxLinkDestinationLength)
616+
for stop := start + 1; stop < limit; stop++ {
604617
if source[stop] == '>' && !isBackslashEscapedBytes(source, stop) {
605618
closing := stop + 1
606619
for closing < len(source) && unicode.IsSpace(rune(source[closing])) {
@@ -616,7 +629,8 @@ func inlineDestinationSpan(source []byte, start int) (sourceSpan, int, bool) {
616629
}
617630

618631
parentheses := 0
619-
for stop := start; stop < len(source); {
632+
limit := min(len(source), start+maxLinkDestinationLength)
633+
for stop := start; stop < limit; {
620634
r, size := utf8.DecodeRune(source[stop:])
621635
if r == '(' || r == ')' {
622636
if !isBackslashEscapedBytes(source, stop) {

‎pkg/sanitize/sanitize_test.go‎

Lines changed: 35 additions & 0 deletions

0 commit comments

Comments
 (0)