Skip to content

Commit 60da2d9

Browse files
committed
Add tests for authenticity verification logic
1 parent 268b037 commit 60da2d9

6 files changed

Lines changed: 231 additions & 1 deletion

File tree

core/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ go_test(
3232
embed = [":core"],
3333
deps = [
3434
"//config",
35+
"//httputil/httputil_test_helper",
3536
"//platforms",
3637
],
3738
)

core/core_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"testing"
1515

1616
"github.com/bazelbuild/bazelisk/config"
17+
"github.com/bazelbuild/bazelisk/httputil/httputil_test_helper"
1718
"github.com/bazelbuild/bazelisk/platforms"
1819
)
1920

@@ -888,3 +889,83 @@ func TestRunBazeliskWithStderrRedirection(t *testing.T) {
888889
t.Error("stdout content should not appear in stderr")
889890
}
890891
}
892+
893+
func TestVerifyBinaryAuthenticity(t *testing.T) {
894+
tmpDir, err := os.MkdirTemp("", "TestVerifyBinaryAuthenticity")
895+
if err != nil {
896+
t.Fatalf("Failed to create temp dir: %v", err)
897+
}
898+
defer os.RemoveAll(tmpDir)
899+
900+
key, err := httputil_test_helper.GenerateTestKey("Bazelisk Test", "test@bazel.build")
901+
if err != nil {
902+
t.Fatalf("Failed to generate test key: %v", err)
903+
}
904+
905+
binaryPath := filepath.Join(tmpDir, "bazel")
906+
content := []byte("binary content")
907+
if err := os.WriteFile(binaryPath, content, 0644); err != nil {
908+
t.Fatalf("Failed to write binary: %v", err)
909+
}
910+
911+
signature, err := httputil_test_helper.SignMessage(content, key)
912+
if err != nil {
913+
t.Fatalf("Failed to sign message: %v", err)
914+
}
915+
signaturePath := binaryPath + ".sig"
916+
if err := os.WriteFile(signaturePath, []byte(signature), 0644); err != nil {
917+
t.Fatalf("Failed to write signature: %v", err)
918+
}
919+
920+
keyPath := filepath.Join(tmpDir, "key.pub")
921+
if err := os.WriteFile(keyPath, []byte(key), 0644); err != nil {
922+
t.Fatalf("Failed to write key: %v", err)
923+
}
924+
925+
t.Run("ValidSignatureWithKeyFile", func(t *testing.T) {
926+
cfg := config.Static(map[string]string{
927+
"BAZELISK_VERIFICATION_KEY_FILE": keyPath,
928+
})
929+
err := verifyBinaryAuthenticity(binaryPath, signaturePath, cfg)
930+
if err != nil {
931+
t.Errorf("verifyBinaryAuthenticity failed: %v", err)
932+
}
933+
})
934+
935+
t.Run("NoSignatureVerification", func(t *testing.T) {
936+
cfg := config.Static(map[string]string{
937+
"BAZELISK_NO_SIGNATURE_VERIFICATION": "1",
938+
})
939+
// Use invalid signature path, should still pass because verification is skipped
940+
err := verifyBinaryAuthenticity(binaryPath, "nonexistent.sig", cfg)
941+
if err != nil {
942+
t.Errorf("verifyBinaryAuthenticity should have skipped verification: %v", err)
943+
}
944+
})
945+
946+
t.Run("InvalidSignature", func(t *testing.T) {
947+
cfg := config.Static(map[string]string{
948+
"BAZELISK_VERIFICATION_KEY_FILE": keyPath,
949+
})
950+
wrongContent := []byte("wrong content")
951+
wrongBinaryPath := filepath.Join(tmpDir, "bazel_wrong")
952+
if err := os.WriteFile(wrongBinaryPath, wrongContent, 0644); err != nil {
953+
t.Fatalf("Failed to write wrong binary: %v", err)
954+
}
955+
956+
err := verifyBinaryAuthenticity(wrongBinaryPath, signaturePath, cfg)
957+
if err == nil {
958+
t.Error("Expected error for invalid signature, but got none")
959+
}
960+
})
961+
962+
t.Run("MissingKeyFile", func(t *testing.T) {
963+
cfg := config.Static(map[string]string{
964+
"BAZELISK_VERIFICATION_KEY_FILE": "nonexistent.key",
965+
})
966+
err := verifyBinaryAuthenticity(binaryPath, signaturePath, cfg)
967+
if err == nil {
968+
t.Error("Expected error for missing key file, but got none")
969+
}
970+
})
971+
}

httputil/BUILD

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ go_library(
2323

2424
go_test(
2525
name = "httputil_test",
26-
srcs = ["httputil_test.go"],
26+
srcs = [
27+
"httputil_test.go",
28+
],
2729
embed = [":httputil"],
30+
deps = [
31+
"//httputil/httputil_test_helper",
32+
],
2833
)

httputil/httputil_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"strings"
88
"testing"
99
"time"
10+
11+
"github.com/bazelbuild/bazelisk/httputil/httputil_test_helper"
1012
)
1113

1214
var (
@@ -251,3 +253,76 @@ func TestNoRetryOnPermanentError(t *testing.T) {
251253
t.Fatalf("Expected no retries for permanent error, but got %d", clock.TimesSlept())
252254
}
253255
}
256+
257+
func TestVerifyBinary(t *testing.T) {
258+
key, err := httputil_test_helper.GenerateTestKey("Bazelisk Test", "test@bazel.build")
259+
if err != nil {
260+
t.Fatalf("Failed to generate test key: %v", err)
261+
}
262+
263+
content := []byte("important content")
264+
signature, err := httputil_test_helper.SignMessage(content, key)
265+
if err != nil {
266+
t.Fatalf("Failed to sign message: %v", err)
267+
}
268+
269+
t.Run("ValidSignature", func(t *testing.T) {
270+
res, err := VerifyBinary(strings.NewReader(string(content)), strings.NewReader(signature), key)
271+
if err != nil {
272+
t.Fatalf("VerifyBinary failed: %v", err)
273+
}
274+
if err := res.SignatureError(); err != nil {
275+
t.Fatalf("Signature error: %v", err)
276+
}
277+
})
278+
279+
t.Run("InvalidSignature", func(t *testing.T) {
280+
res, err := VerifyBinary(strings.NewReader("different content"), strings.NewReader(signature), key)
281+
if err != nil {
282+
// VerifyBinary might return an error or a result with a signature error
283+
return
284+
}
285+
if err := res.SignatureError(); err == nil {
286+
t.Fatal("Expected signature error for different content, but got none")
287+
}
288+
})
289+
290+
t.Run("InvalidKey", func(t *testing.T) {
291+
otherKey, _ := httputil_test_helper.GenerateTestKey("Other Key", "other@example.com")
292+
_, err := VerifyBinary(strings.NewReader(string(content)), strings.NewReader(signature), otherKey)
293+
if err == nil {
294+
// In some cases it might return a result with error instead of error
295+
// but usually failing to find the key in keyring for signature is an error or sig error
296+
}
297+
})
298+
}
299+
300+
func TestVerifyBinaryExpiredKey(t *testing.T) {
301+
key, err := httputil_test_helper.GetExpiredTestKey("Expired", "expired@example.com")
302+
if err != nil {
303+
t.Fatalf("Failed to generate expired key: %v", err)
304+
}
305+
306+
content := []byte("content")
307+
signature, err := httputil_test_helper.SignMessage(content, key)
308+
if err != nil {
309+
t.Fatalf("Failed to sign message: %v", err)
310+
}
311+
312+
// Wait for the key to expire
313+
time.Sleep(2 * time.Second)
314+
315+
res, err := VerifyBinary(strings.NewReader(string(content)), strings.NewReader(signature), key)
316+
if err != nil {
317+
t.Fatalf("VerifyBinary failed: %v", err)
318+
}
319+
320+
// VerifyResult from gopenpgp v3 might not check expiration by default unless configured.
321+
// However, bazelisk's VerifyBinary calls result.SignatureError() which we check.
322+
// It seems gopenpgp v3 might need explicit time setting in verifier if we want to test expiration
323+
// with a faked time, but here we use real sleep.
324+
325+
if err := res.SignatureError(); err == nil {
326+
t.Fatalf("Expected signature error for expired key, but got none")
327+
}
328+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "httputil_test_helper",
5+
testonly = True,
6+
srcs = [
7+
"httputil_test_helper.go",
8+
],
9+
importpath = "github.com/bazelbuild/bazelisk/httputil/httputil_test_helper",
10+
visibility = ["//visibility:public"],
11+
deps = [
12+
"@com_github_protonmail_gopenpgp_v3//crypto",
13+
],
14+
)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package httputil_test_helper
2+
3+
import (
4+
"github.com/ProtonMail/gopenpgp/v3/crypto"
5+
)
6+
7+
func GenerateTestKey(name, email string) (string, error) {
8+
pgp := crypto.PGP()
9+
handle := pgp.KeyGeneration().
10+
AddUserId(name, email).
11+
New()
12+
key, err := handle.GenerateKey()
13+
if err != nil {
14+
return "", err
15+
}
16+
return key.Armor()
17+
}
18+
19+
func SignMessage(message []byte, armoredKey string) (string, error) {
20+
pgp := crypto.PGP()
21+
key, err := crypto.NewKeyFromArmored(armoredKey)
22+
if err != nil {
23+
return "", err
24+
}
25+
keyring, err := crypto.NewKeyRing(key)
26+
if err != nil {
27+
return "", err
28+
}
29+
signer, err := pgp.Sign().
30+
SigningKeys(keyring).
31+
Detached().
32+
New()
33+
if err != nil {
34+
return "", err
35+
}
36+
signature, err := signer.Sign(message, crypto.Armor)
37+
if err != nil {
38+
return "", err
39+
}
40+
return string(signature), nil
41+
}
42+
43+
func GetExpiredTestKey(name, email string) (string, error) {
44+
pgp := crypto.PGP()
45+
handle := pgp.KeyGeneration().
46+
AddUserId(name, email).
47+
Lifetime(1).
48+
New()
49+
key, err := handle.GenerateKey()
50+
if err != nil {
51+
return "", err
52+
}
53+
return key.Armor()
54+
}

0 commit comments

Comments
 (0)