mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:34:19 -04:00
Auto-update logic moved out of the UI into a dedicated updatemanager.Manager service that runs in the connection layer. The UI no longer polls or checks for updates independently. The update manager supports three modes driven by the management server's auto-update policy: No policy set by mgm: checks GitHub for the latest version and notifies the user (previous behavior, now centralized) mgm enforces update: the "About" menu triggers installation directly instead of just downloading the file — user still initiates the action mgm forces update: installation proceeds automatically without user interaction updateManager lifecycle is now owned by daemon, giving the daemon server direct control via a new TriggerUpdate RPC Introduces EngineServices struct to group external service dependencies passed to NewEngine, reducing its argument count from 11 to 4
529 lines
16 KiB
Go
529 lines
16 KiB
Go
package reposign
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Test ArtifactVerify construction
|
|
|
|
func TestArtifactVerify_Construction(t *testing.T) {
|
|
// Generate test root key
|
|
rootKey, _, rootPubPEM, err := GenerateRootKey(365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
rootPubKey, _, err := parsePublicKey(rootPubPEM, tagRootPublic)
|
|
require.NoError(t, err)
|
|
|
|
keysBaseURL := "http://localhost:8080/artifact-signatures"
|
|
|
|
av, err := newArtifactVerify(keysBaseURL, []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
assert.NotNil(t, av)
|
|
assert.NotEmpty(t, av.rootKeys)
|
|
assert.Equal(t, keysBaseURL, av.keysBaseURL.String())
|
|
|
|
// Verify root key structure
|
|
assert.NotEmpty(t, av.rootKeys[0].Key)
|
|
assert.Equal(t, rootKey.Metadata.ID, av.rootKeys[0].Metadata.ID)
|
|
assert.False(t, av.rootKeys[0].Metadata.CreatedAt.IsZero())
|
|
}
|
|
|
|
func TestArtifactVerify_MultipleRootKeys(t *testing.T) {
|
|
// Generate multiple test root keys
|
|
rootKey1, _, rootPubPEM1, err := GenerateRootKey(365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
rootPubKey1, _, err := parsePublicKey(rootPubPEM1, tagRootPublic)
|
|
require.NoError(t, err)
|
|
|
|
rootKey2, _, rootPubPEM2, err := GenerateRootKey(365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
rootPubKey2, _, err := parsePublicKey(rootPubPEM2, tagRootPublic)
|
|
require.NoError(t, err)
|
|
|
|
keysBaseURL := "http://localhost:8080/artifact-signatures"
|
|
|
|
av, err := newArtifactVerify(keysBaseURL, []PublicKey{rootPubKey1, rootPubKey2})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, av.rootKeys, 2)
|
|
assert.NotEqual(t, rootKey1.Metadata.ID, rootKey2.Metadata.ID)
|
|
}
|
|
|
|
// Test Verify workflow with mock HTTP server
|
|
|
|
func TestArtifactVerify_FullWorkflow(t *testing.T) {
|
|
// Create temporary test directory
|
|
tempDir := t.TempDir()
|
|
|
|
// Step 1: Generate root key
|
|
rootKey, _, _, err := GenerateRootKey(10 * 365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
// Step 2: Generate artifact key
|
|
artifactKey, _, artifactPubPEM, _, err := GenerateArtifactKey(rootKey, 30*24*time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
artifactPubKey, err := ParseArtifactPubKey(artifactPubPEM)
|
|
require.NoError(t, err)
|
|
|
|
// Step 3: Create revocation list
|
|
revocationData, revocationSig, err := CreateRevocationList(*rootKey, defaultRevocationListExpiration)
|
|
require.NoError(t, err)
|
|
|
|
// Step 4: Bundle artifact keys
|
|
artifactKeysBundle, artifactKeysSig, err := BundleArtifactKeys(rootKey, []PublicKey{artifactPubKey})
|
|
require.NoError(t, err)
|
|
|
|
// Step 5: Create test artifact
|
|
artifactPath := filepath.Join(tempDir, "test-artifact.bin")
|
|
artifactData := []byte("This is test artifact data for verification")
|
|
err = os.WriteFile(artifactPath, artifactData, 0644)
|
|
require.NoError(t, err)
|
|
|
|
// Step 6: Sign artifact
|
|
artifactSigData, err := SignData(*artifactKey, artifactData)
|
|
require.NoError(t, err)
|
|
|
|
// Step 7: Setup mock HTTP server
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/artifact-signatures/keys/" + revocationFileName:
|
|
_, _ = w.Write(revocationData)
|
|
case "/artifact-signatures/keys/" + revocationSignFileName:
|
|
_, _ = w.Write(revocationSig)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysFileName:
|
|
_, _ = w.Write(artifactKeysBundle)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysSigFileName:
|
|
_, _ = w.Write(artifactKeysSig)
|
|
case "/artifacts/v1.0.0/test-artifact.bin":
|
|
_, _ = w.Write(artifactData)
|
|
case "/artifact-signatures/tag/v1.0.0/test-artifact.bin.sig":
|
|
_, _ = w.Write(artifactSigData)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Step 8: Create ArtifactVerify with test root key
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
av, err := newArtifactVerify(server.URL+"/artifact-signatures", []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
// Step 9: Verify artifact
|
|
ctx := context.Background()
|
|
err = av.Verify(ctx, "1.0.0", artifactPath)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestArtifactVerify_InvalidRevocationList(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
artifactPath := filepath.Join(tempDir, "test.bin")
|
|
err := os.WriteFile(artifactPath, []byte("test"), 0644)
|
|
require.NoError(t, err)
|
|
|
|
// Setup server with invalid revocation list
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/artifact-signatures/keys/" + revocationFileName:
|
|
_, _ = w.Write([]byte("invalid data"))
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
rootKey, _, _, err := GenerateRootKey(365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
av, err := newArtifactVerify(server.URL+"/artifact-signatures", []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
err = av.Verify(ctx, "1.0.0", artifactPath)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to load revocation list")
|
|
}
|
|
|
|
func TestArtifactVerify_MissingArtifactFile(t *testing.T) {
|
|
rootKey, _, _, err := GenerateRootKey(365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
// Create revocation list
|
|
revocationData, revocationSig, err := CreateRevocationList(*rootKey, defaultRevocationListExpiration)
|
|
require.NoError(t, err)
|
|
|
|
artifactKey, _, artifactPubPEM, _, err := GenerateArtifactKey(rootKey, 30*24*time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
artifactPubKey, err := ParseArtifactPubKey(artifactPubPEM)
|
|
require.NoError(t, err)
|
|
|
|
artifactKeysBundle, artifactKeysSig, err := BundleArtifactKeys(rootKey, []PublicKey{artifactPubKey})
|
|
require.NoError(t, err)
|
|
|
|
// Create signature for non-existent file
|
|
testData := []byte("test")
|
|
artifactSigData, err := SignData(*artifactKey, testData)
|
|
require.NoError(t, err)
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/artifact-signatures/keys/" + revocationFileName:
|
|
_, _ = w.Write(revocationData)
|
|
case "/artifact-signatures/keys/" + revocationSignFileName:
|
|
_, _ = w.Write(revocationSig)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysFileName:
|
|
_, _ = w.Write(artifactKeysBundle)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysSigFileName:
|
|
_, _ = w.Write(artifactKeysSig)
|
|
case "/artifact-signatures/tag/v1.0.0/missing.bin.sig":
|
|
_, _ = w.Write(artifactSigData)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
av, err := newArtifactVerify(server.URL+"/artifact-signatures", []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
err = av.Verify(ctx, "1.0.0", "file.bin")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestArtifactVerify_ServerUnavailable(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
artifactPath := filepath.Join(tempDir, "test.bin")
|
|
err := os.WriteFile(artifactPath, []byte("test"), 0644)
|
|
require.NoError(t, err)
|
|
|
|
rootKey, _, _, err := GenerateRootKey(365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
// Use URL that doesn't exist
|
|
av, err := newArtifactVerify("http://localhost:19999/keys", []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
defer cancel()
|
|
|
|
err = av.Verify(ctx, "1.0.0", artifactPath)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestArtifactVerify_ContextCancellation(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
artifactPath := filepath.Join(tempDir, "test.bin")
|
|
err := os.WriteFile(artifactPath, []byte("test"), 0644)
|
|
require.NoError(t, err)
|
|
|
|
// Create a server that delays response
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
time.Sleep(500 * time.Millisecond)
|
|
_, _ = w.Write([]byte("data"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
rootKey, _, _, err := GenerateRootKey(365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
av, err := newArtifactVerify(server.URL, []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
// Create context that cancels quickly
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
|
defer cancel()
|
|
|
|
err = av.Verify(ctx, "1.0.0", artifactPath)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestArtifactVerify_WithRevocation(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Generate root key
|
|
rootKey, _, _, err := GenerateRootKey(10 * 365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
// Generate two artifact keys
|
|
artifactKey1, _, artifactPubPEM1, _, err := GenerateArtifactKey(rootKey, 30*24*time.Hour)
|
|
require.NoError(t, err)
|
|
artifactPubKey1, err := ParseArtifactPubKey(artifactPubPEM1)
|
|
require.NoError(t, err)
|
|
|
|
_, _, artifactPubPEM2, _, err := GenerateArtifactKey(rootKey, 30*24*time.Hour)
|
|
require.NoError(t, err)
|
|
artifactPubKey2, err := ParseArtifactPubKey(artifactPubPEM2)
|
|
require.NoError(t, err)
|
|
|
|
// Create revocation list with first key revoked
|
|
emptyRevocation, _, err := CreateRevocationList(*rootKey, defaultRevocationListExpiration)
|
|
require.NoError(t, err)
|
|
|
|
parsedRevocation, err := ParseRevocationList(emptyRevocation)
|
|
require.NoError(t, err)
|
|
|
|
revocationData, revocationSig, err := ExtendRevocationList(*rootKey, *parsedRevocation, artifactPubKey1.Metadata.ID, defaultRevocationListExpiration)
|
|
require.NoError(t, err)
|
|
|
|
// Bundle both keys
|
|
artifactKeysBundle, artifactKeysSig, err := BundleArtifactKeys(rootKey, []PublicKey{artifactPubKey1, artifactPubKey2})
|
|
require.NoError(t, err)
|
|
|
|
// Create artifact signed by revoked key
|
|
artifactPath := filepath.Join(tempDir, "test.bin")
|
|
artifactData := []byte("test data")
|
|
err = os.WriteFile(artifactPath, artifactData, 0644)
|
|
require.NoError(t, err)
|
|
|
|
artifactSigData, err := SignData(*artifactKey1, artifactData)
|
|
require.NoError(t, err)
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/artifact-signatures/keys/" + revocationFileName:
|
|
_, _ = w.Write(revocationData)
|
|
case "/artifact-signatures/keys/" + revocationSignFileName:
|
|
_, _ = w.Write(revocationSig)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysFileName:
|
|
_, _ = w.Write(artifactKeysBundle)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysSigFileName:
|
|
_, _ = w.Write(artifactKeysSig)
|
|
case "/artifact-signatures/tag/v1.0.0/test.bin.sig":
|
|
_, _ = w.Write(artifactSigData)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
av, err := newArtifactVerify(server.URL+"/artifact-signatures", []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
err = av.Verify(ctx, "1.0.0", artifactPath)
|
|
// Should fail because the signing key is revoked
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "no signing Key found")
|
|
}
|
|
|
|
func TestArtifactVerify_ValidWithSecondKey(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Generate root key
|
|
rootKey, _, _, err := GenerateRootKey(10 * 365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
// Generate two artifact keys
|
|
_, _, artifactPubPEM1, _, err := GenerateArtifactKey(rootKey, 30*24*time.Hour)
|
|
require.NoError(t, err)
|
|
artifactPubKey1, err := ParseArtifactPubKey(artifactPubPEM1)
|
|
require.NoError(t, err)
|
|
|
|
artifactKey2, _, artifactPubPEM2, _, err := GenerateArtifactKey(rootKey, 30*24*time.Hour)
|
|
require.NoError(t, err)
|
|
artifactPubKey2, err := ParseArtifactPubKey(artifactPubPEM2)
|
|
require.NoError(t, err)
|
|
|
|
// Create revocation list with first key revoked
|
|
emptyRevocation, _, err := CreateRevocationList(*rootKey, defaultRevocationListExpiration)
|
|
require.NoError(t, err)
|
|
|
|
parsedRevocation, err := ParseRevocationList(emptyRevocation)
|
|
require.NoError(t, err)
|
|
|
|
revocationData, revocationSig, err := ExtendRevocationList(*rootKey, *parsedRevocation, artifactPubKey1.Metadata.ID, defaultRevocationListExpiration)
|
|
require.NoError(t, err)
|
|
|
|
// Bundle both keys
|
|
artifactKeysBundle, artifactKeysSig, err := BundleArtifactKeys(rootKey, []PublicKey{artifactPubKey1, artifactPubKey2})
|
|
require.NoError(t, err)
|
|
|
|
// Create artifact signed by second key (not revoked)
|
|
artifactPath := filepath.Join(tempDir, "test.bin")
|
|
artifactData := []byte("test data")
|
|
err = os.WriteFile(artifactPath, artifactData, 0644)
|
|
require.NoError(t, err)
|
|
|
|
artifactSigData, err := SignData(*artifactKey2, artifactData)
|
|
require.NoError(t, err)
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/artifact-signatures/keys/" + revocationFileName:
|
|
_, _ = w.Write(revocationData)
|
|
case "/artifact-signatures/keys/" + revocationSignFileName:
|
|
_, _ = w.Write(revocationSig)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysFileName:
|
|
_, _ = w.Write(artifactKeysBundle)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysSigFileName:
|
|
_, _ = w.Write(artifactKeysSig)
|
|
case "/artifact-signatures/tag/v1.0.0/test.bin.sig":
|
|
_, _ = w.Write(artifactSigData)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
av, err := newArtifactVerify(server.URL+"/artifact-signatures", []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
err = av.Verify(ctx, "1.0.0", artifactPath)
|
|
// Should succeed because second key is not revoked
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestArtifactVerify_TamperedArtifact(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
|
|
// Generate root key and artifact key
|
|
rootKey, _, _, err := GenerateRootKey(10 * 365 * 24 * time.Hour)
|
|
require.NoError(t, err)
|
|
|
|
artifactKey, _, artifactPubPEM, _, err := GenerateArtifactKey(rootKey, 30*24*time.Hour)
|
|
require.NoError(t, err)
|
|
artifactPubKey, err := ParseArtifactPubKey(artifactPubPEM)
|
|
require.NoError(t, err)
|
|
|
|
// Create revocation list
|
|
revocationData, revocationSig, err := CreateRevocationList(*rootKey, defaultRevocationListExpiration)
|
|
require.NoError(t, err)
|
|
|
|
// Bundle keys
|
|
artifactKeysBundle, artifactKeysSig, err := BundleArtifactKeys(rootKey, []PublicKey{artifactPubKey})
|
|
require.NoError(t, err)
|
|
|
|
// Sign original data
|
|
originalData := []byte("original data")
|
|
artifactSigData, err := SignData(*artifactKey, originalData)
|
|
require.NoError(t, err)
|
|
|
|
// Write tampered data to file
|
|
artifactPath := filepath.Join(tempDir, "test.bin")
|
|
tamperedData := []byte("tampered data")
|
|
err = os.WriteFile(artifactPath, tamperedData, 0644)
|
|
require.NoError(t, err)
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/artifact-signatures/keys/" + revocationFileName:
|
|
_, _ = w.Write(revocationData)
|
|
case "/artifact-signatures/keys/" + revocationSignFileName:
|
|
_, _ = w.Write(revocationSig)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysFileName:
|
|
_, _ = w.Write(artifactKeysBundle)
|
|
case "/artifact-signatures/keys/" + artifactPubKeysSigFileName:
|
|
_, _ = w.Write(artifactKeysSig)
|
|
case "/artifact-signatures/tag/v1.0.0/test.bin.sig":
|
|
_, _ = w.Write(artifactSigData)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
rootPubKey := PublicKey{
|
|
Key: rootKey.Key.Public().(ed25519.PublicKey),
|
|
Metadata: rootKey.Metadata,
|
|
}
|
|
|
|
av, err := newArtifactVerify(server.URL+"/artifact-signatures", []PublicKey{rootPubKey})
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
err = av.Verify(ctx, "1.0.0", artifactPath)
|
|
// Should fail because artifact was tampered
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "failed to validate artifact")
|
|
}
|
|
|
|
// Test URL validation
|
|
|
|
func TestArtifactVerify_URLParsing(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
keysBaseURL string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Valid HTTP URL",
|
|
keysBaseURL: "http://example.com/artifact-signatures",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Valid HTTPS URL",
|
|
keysBaseURL: "https://example.com/artifact-signatures",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "URL with port",
|
|
keysBaseURL: "http://localhost:8080/artifact-signatures",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Invalid URL",
|
|
keysBaseURL: "://invalid",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := newArtifactVerify(tt.keysBaseURL, nil)
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|