mirror of
https://github.com/harness/drone.git
synced 2025-09-04 19:37:44 +00:00
* empty commit * Merge remote-tracking branch 'origin/main' into mg/publickey/verify * addressing PR comments * addressing PR comments * addressing PR comments * commit signature parsing
363 lines
11 KiB
Go
363 lines
11 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package parser
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/harness/gitness/app/services/publickey/keyssh"
|
|
"github.com/harness/gitness/git/sha"
|
|
"github.com/harness/gitness/types"
|
|
"github.com/harness/gitness/types/enum"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
func TestObject(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data string
|
|
want ObjectRaw
|
|
}{
|
|
{
|
|
name: "empty",
|
|
data: "",
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{},
|
|
Message: "",
|
|
},
|
|
},
|
|
{
|
|
name: "no_header",
|
|
data: "\nline1\nline2\n",
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{},
|
|
Message: "line1\nline2\n",
|
|
},
|
|
},
|
|
{
|
|
name: "no_body",
|
|
data: "header 1\nheader 2\n",
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{
|
|
{Type: "header", Value: "1\n"},
|
|
{Type: "header", Value: "2\n"},
|
|
},
|
|
Message: "",
|
|
},
|
|
},
|
|
{
|
|
name: "dummy_content",
|
|
data: "header 1\nheader 2\n\nblah blah\nblah",
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{
|
|
{Type: "header", Value: "1\n"},
|
|
{Type: "header", Value: "2\n"},
|
|
},
|
|
Message: "blah blah\nblah",
|
|
},
|
|
},
|
|
{
|
|
name: "dummy_content_multiline_header",
|
|
data: "header-simple 1\nheader-multiline line1\n line2\nheader-three blah\n\nblah blah\nblah",
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{
|
|
{Type: "header-simple", Value: "1\n"},
|
|
{Type: "header-multiline", Value: "line1\nline2\n"},
|
|
{Type: "header-three", Value: "blah\n"},
|
|
},
|
|
Message: "blah blah\nblah",
|
|
},
|
|
},
|
|
{
|
|
name: "simple_commit",
|
|
data: `tree a32348a67ba786383cedddccd79944992e1656b9
|
|
parent 286d9081dfddd0b95e43f98f32984b782678fc43
|
|
author Marko Gaćeša <marko.gacesa@harness.io> 1748009627 +0200
|
|
committer Marko Gaćeša <marko.gacesa@harness.io> 1748012917 +0200
|
|
|
|
Test commit
|
|
`,
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{
|
|
{Type: "tree", Value: "a32348a67ba786383cedddccd79944992e1656b9\n"},
|
|
{Type: "parent", Value: "286d9081dfddd0b95e43f98f32984b782678fc43\n"},
|
|
{Type: "author", Value: "Marko Gaćeša <marko.gacesa@harness.io> 1748009627 +0200\n"},
|
|
{Type: "committer", Value: "Marko Gaćeša <marko.gacesa@harness.io> 1748012917 +0200\n"},
|
|
},
|
|
Message: "Test commit\n",
|
|
SignedContent: nil,
|
|
Signature: nil,
|
|
SignatureType: "",
|
|
},
|
|
},
|
|
{
|
|
name: "signed_commit",
|
|
data: `tree 1e6502c1add2beb75875d261ca28abdf6e3d9091
|
|
parent a74b6a06bcf7f0d7b902af492826c20f9835a932
|
|
author Marko Gaćeša <marko.gacesa@harness.io> 1749221807 +0200
|
|
committer Marko Gaćeša <marko.gacesa@harness.io> 1749221807 +0200
|
|
gpgsig -----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQDJwTh2XHcewg3MXY8hxnH1WuSAjuQPzcjaoX0Q1x923k4y2Y2hXd/cN6l+PdGo71B
|
|
8+HfQ6jFa7/UU4cZu4QAc=
|
|
-----END SSH SIGNATURE-----
|
|
|
|
this is a commit message
|
|
`,
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{
|
|
{Type: "tree", Value: "1e6502c1add2beb75875d261ca28abdf6e3d9091\n"},
|
|
{Type: "parent", Value: "a74b6a06bcf7f0d7b902af492826c20f9835a932\n"},
|
|
{Type: "author", Value: "Marko Gaćeša <marko.gacesa@harness.io> 1749221807 +0200\n"},
|
|
{Type: "committer", Value: "Marko Gaćeša <marko.gacesa@harness.io> 1749221807 +0200\n"},
|
|
},
|
|
Message: "this is a commit message\n",
|
|
SignedContent: []byte(`tree 1e6502c1add2beb75875d261ca28abdf6e3d9091
|
|
parent a74b6a06bcf7f0d7b902af492826c20f9835a932
|
|
author Marko Gaćeša <marko.gacesa@harness.io> 1749221807 +0200
|
|
committer Marko Gaćeša <marko.gacesa@harness.io> 1749221807 +0200
|
|
|
|
this is a commit message
|
|
`),
|
|
Signature: []byte(`-----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQDJwTh2XHcewg3MXY8hxnH1WuSAjuQPzcjaoX0Q1x923k4y2Y2hXd/cN6l+PdGo71B
|
|
8+HfQ6jFa7/UU4cZu4QAc=
|
|
-----END SSH SIGNATURE-----
|
|
`),
|
|
SignatureType: "SSH SIGNATURE",
|
|
},
|
|
},
|
|
{
|
|
name: "signed_tag",
|
|
data: `object 7a56ee7136c7d4882f88db68c0e629b81a47bfc9
|
|
type commit
|
|
tag test
|
|
tagger Marko Gaćeša <marko.gacesa@harness.io> 1749035203 +0200
|
|
|
|
This is a test tag
|
|
-----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQDzfgUo2uoz/VCuv74QnweB16XS6FGmaDkefMcVpYJdz88WRG99yhmYC0ca6QYiaj4
|
|
ttNpubwUBQRPTo8z5Aows=
|
|
-----END SSH SIGNATURE-----
|
|
`,
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{
|
|
{Type: "object", Value: "7a56ee7136c7d4882f88db68c0e629b81a47bfc9\n"},
|
|
{Type: "type", Value: "commit\n"},
|
|
{Type: "tag", Value: "test\n"},
|
|
{Type: "tagger", Value: "Marko Gaćeša <marko.gacesa@harness.io> 1749035203 +0200\n"},
|
|
},
|
|
Message: "This is a test tag\n",
|
|
SignedContent: []byte(`object 7a56ee7136c7d4882f88db68c0e629b81a47bfc9
|
|
type commit
|
|
tag test
|
|
tagger Marko Gaćeša <marko.gacesa@harness.io> 1749035203 +0200
|
|
|
|
This is a test tag
|
|
`),
|
|
Signature: []byte(`-----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQDzfgUo2uoz/VCuv74QnweB16XS6FGmaDkefMcVpYJdz88WRG99yhmYC0ca6QYiaj4
|
|
ttNpubwUBQRPTo8z5Aows=
|
|
-----END SSH SIGNATURE-----
|
|
`),
|
|
SignatureType: "SSH SIGNATURE",
|
|
},
|
|
},
|
|
{
|
|
name: "merge_commit",
|
|
data: `tree bf7ecca7c6741453e16a0a92be5d9ccd779abcfa
|
|
parent 04617da8f3215c84ae4af39b8d734c3df2247347
|
|
parent 7077c29016c1be5465678c9ba25983937040dcb2
|
|
author Marko Gaćeša <marko.gacesa@harness.io> 1749219134 +0200
|
|
committer Marko Gaćeša <marko.gacesa@harness.io> 1749219134 +0200
|
|
mergetag object 7077c29016c1be5465678c9ba25983937040dcb2
|
|
type commit
|
|
tag v1.0.0
|
|
tagger Marko Gaćeša <marko.gacesa@harness.io> 1749218976 +0200
|
|
|
|
version 1
|
|
-----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQG0+9xHX8+7AnbkV//QH7ZvrDoUcm6GrqWkTwHmgSqBsMa7X8aXOtcwPwNJvXpOl8E
|
|
prGrumXZoEXzcZMrCG5A0=
|
|
-----END SSH SIGNATURE-----
|
|
gpgsig -----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQJpnz9Dsv6VleulZSzd3/PRGTJoPsUem0Waq4EbSRB7FDewjf11LRkkqNENiivT1pT
|
|
Rv18ZouJpO2LRIXdZpxAE=
|
|
-----END SSH SIGNATURE-----
|
|
|
|
Merge tag 'v1.0.0' into marko
|
|
|
|
version 1
|
|
`,
|
|
want: ObjectRaw{
|
|
Headers: []ObjectHeader{
|
|
{Type: "tree", Value: "bf7ecca7c6741453e16a0a92be5d9ccd779abcfa\n"},
|
|
{Type: "parent", Value: "04617da8f3215c84ae4af39b8d734c3df2247347\n"},
|
|
{Type: "parent", Value: "7077c29016c1be5465678c9ba25983937040dcb2\n"},
|
|
{Type: "author", Value: "Marko Gaćeša <marko.gacesa@harness.io> 1749219134 +0200\n"},
|
|
{Type: "committer", Value: "Marko Gaćeša <marko.gacesa@harness.io> 1749219134 +0200\n"},
|
|
{Type: "mergetag", Value: `object 7077c29016c1be5465678c9ba25983937040dcb2
|
|
type commit
|
|
tag v1.0.0
|
|
tagger Marko Gaćeša <marko.gacesa@harness.io> 1749218976 +0200
|
|
|
|
version 1
|
|
-----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQG0+9xHX8+7AnbkV//QH7ZvrDoUcm6GrqWkTwHmgSqBsMa7X8aXOtcwPwNJvXpOl8E
|
|
prGrumXZoEXzcZMrCG5A0=
|
|
-----END SSH SIGNATURE-----
|
|
`},
|
|
},
|
|
Message: "Merge tag 'v1.0.0' into marko\n\nversion 1\n",
|
|
SignedContent: []byte(`tree bf7ecca7c6741453e16a0a92be5d9ccd779abcfa
|
|
parent 04617da8f3215c84ae4af39b8d734c3df2247347
|
|
parent 7077c29016c1be5465678c9ba25983937040dcb2
|
|
author Marko Gaćeša <marko.gacesa@harness.io> 1749219134 +0200
|
|
committer Marko Gaćeša <marko.gacesa@harness.io> 1749219134 +0200
|
|
mergetag object 7077c29016c1be5465678c9ba25983937040dcb2
|
|
type commit
|
|
tag v1.0.0
|
|
tagger Marko Gaćeša <marko.gacesa@harness.io> 1749218976 +0200
|
|
|
|
version 1
|
|
-----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQG0+9xHX8+7AnbkV//QH7ZvrDoUcm6GrqWkTwHmgSqBsMa7X8aXOtcwPwNJvXpOl8E
|
|
prGrumXZoEXzcZMrCG5A0=
|
|
-----END SSH SIGNATURE-----
|
|
|
|
Merge tag 'v1.0.0' into marko
|
|
|
|
version 1
|
|
`),
|
|
Signature: []byte(`-----BEGIN SSH SIGNATURE-----
|
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEM1i8vha2gQ/ZXHinPejh0hS4C
|
|
x8VV1M2uwW6tglOswAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
|
AAAAQJpnz9Dsv6VleulZSzd3/PRGTJoPsUem0Waq4EbSRB7FDewjf11LRkkqNENiivT1pT
|
|
Rv18ZouJpO2LRIXdZpxAE=
|
|
-----END SSH SIGNATURE-----
|
|
`),
|
|
SignatureType: "SSH SIGNATURE",
|
|
},
|
|
},
|
|
}
|
|
|
|
objectSHA := sha.Must("123456789")
|
|
person := types.Signature{
|
|
Identity: types.Identity{Name: "Michelangelo", Email: "michelangelo@harness.io"},
|
|
When: time.Now(),
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
object, err := Object([]byte(test.data))
|
|
if err != nil {
|
|
t.Errorf("failed: %s", err.Error())
|
|
return
|
|
}
|
|
|
|
if diff := cmp.Diff(object, test.want); diff != "" {
|
|
t.Errorf("failed:\n%s\n", diff)
|
|
}
|
|
|
|
if len(object.Signature) == 0 {
|
|
// skip testing signed content because the data doesn't contain a signature
|
|
return
|
|
}
|
|
|
|
ctx := context.Background()
|
|
var verify keyssh.Verify
|
|
|
|
signature := object.Signature
|
|
content := object.SignedContent
|
|
|
|
if status := verify.Parse(ctx, signature, objectSHA); status != "" {
|
|
t.Errorf("failed to extract key from the signature: %s", status)
|
|
}
|
|
|
|
// we use the public key directly from the signature
|
|
publicKey, _ := ssh.ParsePublicKey(verify.SignaturePublicKey())
|
|
pk := ssh.MarshalAuthorizedKey(publicKey)
|
|
|
|
if status := verify.Verify(ctx, pk, content, objectSHA, person); status != enum.GitSignatureGood {
|
|
t.Errorf("failed to verify the signature: %s", status)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestObjectNegative(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data string
|
|
errStr string
|
|
}{
|
|
{
|
|
name: "header_without_EOL",
|
|
data: "header 1\nheader 2",
|
|
errStr: "header line must end with EOL character",
|
|
},
|
|
{
|
|
name: "header_without_value",
|
|
data: "header\n\nbody",
|
|
errStr: "malformed header",
|
|
},
|
|
{
|
|
name: "header_without_type",
|
|
data: " 1\n",
|
|
errStr: "malformed header",
|
|
},
|
|
{
|
|
name: "header_invalid_sig",
|
|
data: "gpgsig this is\n not a sig\n\nbody",
|
|
errStr: "invalid signature header",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, err := Object([]byte(test.data))
|
|
|
|
if err == nil {
|
|
t.Error("expected error but got none")
|
|
return
|
|
}
|
|
|
|
if want, got := test.errStr, err.Error(); !strings.HasPrefix(got, want) {
|
|
t.Errorf("want error message to start with %s, got %s", want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|