mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Fix panic bug in handling multiple references in commit (#13486)
* Fix panic bug in handling multiple references in commit The issue lay in determining the position of matches on a second run round a commit message in FindAllIssueReferences. Fix #13483 Signed-off-by: Andrew Thornton <art27@cantab.net> * Extract function and make testable Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix the comment Signed-off-by: Andrew Thornton <art27@cantab.net> * cleaning up the comments a bit more Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
		@@ -235,40 +235,78 @@ func findAllIssueReferencesMarkdown(content string) []*rawReference {
 | 
				
			|||||||
	return findAllIssueReferencesBytes(bcontent, links)
 | 
						return findAllIssueReferencesBytes(bcontent, links)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func convertFullHTMLReferencesToShortRefs(re *regexp.Regexp, contentBytes *[]byte) {
 | 
				
			||||||
 | 
						// We will iterate through the content, rewrite and simplify full references.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// We want to transform something like:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// this is a https://ourgitea.com/git/owner/repo/issues/123456789, foo
 | 
				
			||||||
 | 
						// https://ourgitea.com/git/owner/repo/pulls/123456789
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Into something like:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// this is a #123456789, foo
 | 
				
			||||||
 | 
						// !123456789
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pos := 0
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							// re looks for something like: (\s|^|\(|\[)https://ourgitea.com/git/(owner/repo)/(issues)/(123456789)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)
 | 
				
			||||||
 | 
							match := re.FindSubmatchIndex((*contentBytes)[pos:])
 | 
				
			||||||
 | 
							if match == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// match is a bunch of indices into the content from pos onwards so
 | 
				
			||||||
 | 
							// to simplify things let's just add pos to all of the indices in match
 | 
				
			||||||
 | 
							for i := range match {
 | 
				
			||||||
 | 
								match[i] += pos
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// match[0]-match[1] is whole string
 | 
				
			||||||
 | 
							// match[2]-match[3] is preamble
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// move the position to the end of the preamble
 | 
				
			||||||
 | 
							pos = match[3]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// match[4]-match[5] is owner/repo
 | 
				
			||||||
 | 
							// now copy the owner/repo to end of the preamble
 | 
				
			||||||
 | 
							endPos := pos + match[5] - match[4]
 | 
				
			||||||
 | 
							copy((*contentBytes)[pos:endPos], (*contentBytes)[match[4]:match[5]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// move the current position to the end of the newly copied owner/repo
 | 
				
			||||||
 | 
							pos = endPos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Now set the issue/pull marker:
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// match[6]-match[7] == 'issues'
 | 
				
			||||||
 | 
							(*contentBytes)[pos] = '#'
 | 
				
			||||||
 | 
							if string((*contentBytes)[match[6]:match[7]]) == "pulls" {
 | 
				
			||||||
 | 
								(*contentBytes)[pos] = '!'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pos++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Then add the issue/pull number
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// match[8]-match[9] is the number
 | 
				
			||||||
 | 
							endPos = pos + match[9] - match[8]
 | 
				
			||||||
 | 
							copy((*contentBytes)[pos:endPos], (*contentBytes)[match[8]:match[9]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Now copy what's left at the end of the string to the new end position
 | 
				
			||||||
 | 
							copy((*contentBytes)[endPos:], (*contentBytes)[match[9]:])
 | 
				
			||||||
 | 
							// now we reset the length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// our new section has length endPos - match[3]
 | 
				
			||||||
 | 
							// our old section has length match[9] - match[3]
 | 
				
			||||||
 | 
							(*contentBytes) = (*contentBytes)[:len((*contentBytes))-match[9]+endPos]
 | 
				
			||||||
 | 
							pos = endPos
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FindAllIssueReferences returns a list of unvalidated references found in a string.
 | 
					// FindAllIssueReferences returns a list of unvalidated references found in a string.
 | 
				
			||||||
func FindAllIssueReferences(content string) []IssueReference {
 | 
					func FindAllIssueReferences(content string) []IssueReference {
 | 
				
			||||||
	// Need to convert fully qualified html references to local system to #/! short codes
 | 
						// Need to convert fully qualified html references to local system to #/! short codes
 | 
				
			||||||
	contentBytes := []byte(content)
 | 
						contentBytes := []byte(content)
 | 
				
			||||||
	if re := getGiteaIssuePullPattern(); re != nil {
 | 
						if re := getGiteaIssuePullPattern(); re != nil {
 | 
				
			||||||
		pos := 0
 | 
							convertFullHTMLReferencesToShortRefs(re, &contentBytes)
 | 
				
			||||||
		for {
 | 
					 | 
				
			||||||
			match := re.FindSubmatchIndex(contentBytes[pos:])
 | 
					 | 
				
			||||||
			if match == nil {
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// match[0]-match[1] is whole string
 | 
					 | 
				
			||||||
			// match[2]-match[3] is preamble
 | 
					 | 
				
			||||||
			pos += match[3]
 | 
					 | 
				
			||||||
			// match[4]-match[5] is owner/repo
 | 
					 | 
				
			||||||
			endPos := pos + match[5] - match[4]
 | 
					 | 
				
			||||||
			copy(contentBytes[pos:endPos], contentBytes[match[4]:match[5]])
 | 
					 | 
				
			||||||
			pos = endPos
 | 
					 | 
				
			||||||
			// match[6]-match[7] == 'issues'
 | 
					 | 
				
			||||||
			contentBytes[pos] = '#'
 | 
					 | 
				
			||||||
			if string(contentBytes[match[6]:match[7]]) == "pulls" {
 | 
					 | 
				
			||||||
				contentBytes[pos] = '!'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			pos++
 | 
					 | 
				
			||||||
			// match[8]-match[9] is the number
 | 
					 | 
				
			||||||
			endPos = pos + match[9] - match[8]
 | 
					 | 
				
			||||||
			copy(contentBytes[pos:endPos], contentBytes[match[8]:match[9]])
 | 
					 | 
				
			||||||
			copy(contentBytes[endPos:], contentBytes[match[9]:])
 | 
					 | 
				
			||||||
			// now we reset the length
 | 
					 | 
				
			||||||
			// our new section has length endPos - match[3]
 | 
					 | 
				
			||||||
			// our old section has length match[9] - match[3]
 | 
					 | 
				
			||||||
			contentBytes = contentBytes[:len(contentBytes)-match[9]+endPos]
 | 
					 | 
				
			||||||
			pos = endPos
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		log.Debug("No GiteaIssuePullPattern pattern")
 | 
							log.Debug("No GiteaIssuePullPattern pattern")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
package references
 | 
					package references
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -29,6 +30,26 @@ type testResult struct {
 | 
				
			|||||||
	TimeLog        string
 | 
						TimeLog        string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConvertFullHTMLReferencesToShortRefs(t *testing.T) {
 | 
				
			||||||
 | 
						re := regexp.MustCompile(`(\s|^|\(|\[)` +
 | 
				
			||||||
 | 
							regexp.QuoteMeta("https://ourgitea.com/git/") +
 | 
				
			||||||
 | 
							`([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+)/` +
 | 
				
			||||||
 | 
							`((?:issues)|(?:pulls))/([0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
 | 
				
			||||||
 | 
						test := `this is a https://ourgitea.com/git/owner/repo/issues/123456789, foo
 | 
				
			||||||
 | 
					https://ourgitea.com/git/owner/repo/pulls/123456789
 | 
				
			||||||
 | 
					  And https://ourgitea.com/git/owner/repo/pulls/123
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
						expect := `this is a owner/repo#123456789, foo
 | 
				
			||||||
 | 
					owner/repo!123456789
 | 
				
			||||||
 | 
					  And owner/repo!123
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contentBytes := []byte(test)
 | 
				
			||||||
 | 
						convertFullHTMLReferencesToShortRefs(re, &contentBytes)
 | 
				
			||||||
 | 
						result := string(contentBytes)
 | 
				
			||||||
 | 
						assert.EqualValues(t, expect, result)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestFindAllIssueReferences(t *testing.T) {
 | 
					func TestFindAllIssueReferences(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fixtures := []testFixture{
 | 
						fixtures := []testFixture{
 | 
				
			||||||
@@ -106,6 +127,13 @@ func TestFindAllIssueReferences(t *testing.T) {
 | 
				
			|||||||
				{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
 | 
									{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"This http://gitea.com:3000/user4/repo5/pulls/202 yes. http://gitea.com:3000/user4/repo5/pulls/203 no",
 | 
				
			||||||
 | 
								[]testResult{
 | 
				
			||||||
 | 
									{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
 | 
				
			||||||
 | 
									{203, "user4", "repo5", "203", true, XRefActionNone, nil, nil, ""},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.",
 | 
								"This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.",
 | 
				
			||||||
			[]testResult{
 | 
								[]testResult{
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user