Refactor commit signature parser (#30228)
To make it more flexible and support SSH signature. The existing tests are not changed, there are also tests covering `parseTagRef` which also calls `parsePayloadSignature` now. Add some new tests to `Test_parseTagData`
This commit is contained in:
@ -6,16 +6,10 @@ package git
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
const (
|
||||
beginpgp = "\n-----BEGIN PGP SIGNATURE-----\n"
|
||||
endpgp = "\n-----END PGP SIGNATURE-----"
|
||||
)
|
||||
|
||||
// Tag represents a Git tag.
|
||||
type Tag struct {
|
||||
Name string
|
||||
@ -24,7 +18,7 @@ type Tag struct {
|
||||
Type string
|
||||
Tagger *Signature
|
||||
Message string
|
||||
Signature *CommitGPGSignature
|
||||
Signature *CommitSignature
|
||||
}
|
||||
|
||||
// Commit return the commit of the tag reference
|
||||
@ -32,6 +26,36 @@ func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) {
|
||||
return gitRepo.getCommit(tag.Object)
|
||||
}
|
||||
|
||||
func parsePayloadSignature(data []byte, messageStart int) (payload, msg, sign string) {
|
||||
pos := messageStart
|
||||
signStart, signEnd := -1, -1
|
||||
for {
|
||||
eol := bytes.IndexByte(data[pos:], '\n')
|
||||
if eol < 0 {
|
||||
break
|
||||
}
|
||||
line := data[pos : pos+eol]
|
||||
signType, hasPrefix := bytes.CutPrefix(line, []byte("-----BEGIN "))
|
||||
signType, hasSuffix := bytes.CutSuffix(signType, []byte(" SIGNATURE-----"))
|
||||
if hasPrefix && hasSuffix {
|
||||
signEndBytes := append([]byte("\n-----END "), signType...)
|
||||
signEndBytes = append(signEndBytes, []byte(" SIGNATURE-----")...)
|
||||
signEnd = bytes.Index(data[pos:], signEndBytes)
|
||||
if signEnd != -1 {
|
||||
signStart = pos
|
||||
signEnd = pos + signEnd + len(signEndBytes)
|
||||
}
|
||||
}
|
||||
pos += eol + 1
|
||||
}
|
||||
|
||||
if signStart != -1 && signEnd != -1 {
|
||||
msgEnd := max(messageStart, signStart-1)
|
||||
return string(data[:msgEnd]), string(data[messageStart:msgEnd]), string(data[signStart:signEnd])
|
||||
}
|
||||
return string(data), string(data[messageStart:]), ""
|
||||
}
|
||||
|
||||
// Parse commit information from the (uncompressed) raw
|
||||
// data from the commit object.
|
||||
// \n\n separate headers from message
|
||||
@ -40,47 +64,37 @@ func parseTagData(objectFormat ObjectFormat, data []byte) (*Tag, error) {
|
||||
tag.ID = objectFormat.EmptyObjectID()
|
||||
tag.Object = objectFormat.EmptyObjectID()
|
||||
tag.Tagger = &Signature{}
|
||||
// we now have the contents of the commit object. Let's investigate...
|
||||
nextline := 0
|
||||
l:
|
||||
|
||||
pos := 0
|
||||
for {
|
||||
eol := bytes.IndexByte(data[nextline:], '\n')
|
||||
switch {
|
||||
case eol > 0:
|
||||
line := data[nextline : nextline+eol]
|
||||
spacepos := bytes.IndexByte(line, ' ')
|
||||
reftype := line[:spacepos]
|
||||
switch string(reftype) {
|
||||
case "object":
|
||||
id, err := NewIDFromString(string(line[spacepos+1:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag.Object = id
|
||||
case "type":
|
||||
// A commit can have one or more parents
|
||||
tag.Type = string(line[spacepos+1:])
|
||||
case "tagger":
|
||||
tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(line[spacepos+1:]))
|
||||
}
|
||||
nextline += eol + 1
|
||||
case eol == 0:
|
||||
tag.Message = string(data[nextline+1:])
|
||||
break l
|
||||
default:
|
||||
break l
|
||||
eol := bytes.IndexByte(data[pos:], '\n')
|
||||
if eol == -1 {
|
||||
break // shouldn't happen, but could just tolerate it
|
||||
}
|
||||
if eol == 0 {
|
||||
pos++
|
||||
break // end of headers
|
||||
}
|
||||
line := data[pos : pos+eol]
|
||||
key, val, _ := bytes.Cut(line, []byte(" "))
|
||||
switch string(key) {
|
||||
case "object":
|
||||
id, err := NewIDFromString(string(val))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag.Object = id
|
||||
case "type":
|
||||
tag.Type = string(val) // A commit can have one or more parents
|
||||
case "tagger":
|
||||
tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(val))
|
||||
}
|
||||
pos += eol + 1
|
||||
}
|
||||
idx := strings.LastIndex(tag.Message, beginpgp)
|
||||
if idx > 0 {
|
||||
endSigIdx := strings.Index(tag.Message[idx:], endpgp)
|
||||
if endSigIdx > 0 {
|
||||
tag.Signature = &CommitGPGSignature{
|
||||
Signature: tag.Message[idx+1 : idx+endSigIdx+len(endpgp)],
|
||||
Payload: string(data[:bytes.LastIndex(data, []byte(beginpgp))+1]),
|
||||
}
|
||||
tag.Message = tag.Message[:idx+1]
|
||||
}
|
||||
payload, msg, sign := parsePayloadSignature(data, pos)
|
||||
tag.Message = msg
|
||||
if len(sign) > 0 {
|
||||
tag.Signature = &CommitSignature{Signature: sign, Payload: payload}
|
||||
}
|
||||
return tag, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user