| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 | package plugin_packagerimport (	"crypto/rsa"	"embed"	"fmt"	"os"	"path/filepath"	"strings"	"testing"	"github.com/langgenius/dify-plugin-daemon/internal/utils/encryption"	"github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/decoder"	"github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/packager"	"github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/signer"	"github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/signer/withkey")//go:embed testdata/manifest.yamlvar manifest []byte//go:embed testdata/neko.yamlvar neko []byte//go:embed testdata/.difyignorevar dify_ignore []byte//go:embed testdata/ignoredvar ignored []byte//go:embed testdata/_assets/test.svgvar test_svg []byte//go:embed testdata/keysvar keys embed.FS// createMinimalPlugin creates a minimal test plugin and returns the zip filefunc createMinimalPlugin(t *testing.T) []byte {	// create a temp directory	tempDir := t.TempDir()	// create basic files	if err := os.WriteFile(filepath.Join(tempDir, "manifest.yaml"), manifest, 0644); err != nil {		t.Errorf("failed to write manifest: %s", err.Error())		return nil	}	if err := os.WriteFile(filepath.Join(tempDir, "neko.yaml"), neko, 0644); err != nil {		t.Errorf("failed to write neko: %s", err.Error())		return nil	}	// create _assets directory and files	if err := os.MkdirAll(filepath.Join(tempDir, "_assets"), 0755); err != nil {		t.Errorf("failed to create _assets directory: %s", err.Error())		return nil	}	if err := os.WriteFile(filepath.Join(tempDir, "_assets/test.svg"), test_svg, 0644); err != nil {		t.Errorf("failed to write test.svg: %s", err.Error())		return nil	}	// create decoder	originDecoder, err := decoder.NewFSPluginDecoder(tempDir)	if err != nil {		t.Errorf("failed to create decoder: %s", err.Error())		return nil	}	// create packager	packager := packager.NewPackager(originDecoder)	// pack	zip, err := packager.Pack(52428800)	if err != nil {		t.Errorf("failed to pack: %s", err.Error())		return nil	}	return zip}func TestPackagerAndVerifier(t *testing.T) {	// create a temp directory	tempDir := t.TempDir()	// create manifest	if err := os.WriteFile(filepath.Join(tempDir, "manifest.yaml"), manifest, 0644); err != nil {		t.Errorf("failed to write manifest: %s", err.Error())		return	}	if err := os.WriteFile(filepath.Join(tempDir, "neko.yaml"), neko, 0644); err != nil {		t.Errorf("failed to write neko: %s", err.Error())		return	}	// create .difyignore	if err := os.WriteFile(filepath.Join(tempDir, ".difyignore"), dify_ignore, 0644); err != nil {		t.Errorf("failed to write .difyignore: %s", err.Error())		return	}	// create ignored	if err := os.WriteFile(filepath.Join(tempDir, "ignored"), ignored, 0644); err != nil {		t.Errorf("failed to write ignored: %s", err.Error())		return	}	// create ignored_paths	if err := os.MkdirAll(filepath.Join(tempDir, "ignored_paths"), 0755); err != nil {		t.Errorf("failed to create ignored_paths directory: %s", err.Error())		return	}	// create ignored_paths/ignored	if err := os.WriteFile(filepath.Join(tempDir, "ignored_paths/ignored"), ignored, 0644); err != nil {		t.Errorf("failed to write ignored_paths/ignored: %s", err.Error())		return	}	if err := os.MkdirAll(filepath.Join(tempDir, "_assets"), 0755); err != nil {		t.Errorf("failed to create _assets directory: %s", err.Error())		return	}	if err := os.WriteFile(filepath.Join(tempDir, "_assets/test.svg"), test_svg, 0644); err != nil {		t.Errorf("failed to write test.svg: %s", err.Error())		return	}	originDecoder, err := decoder.NewFSPluginDecoder(tempDir)	if err != nil {		t.Errorf("failed to create decoder: %s", err.Error())		return	}	// walk	err = originDecoder.Walk(func(filename string, dir string) error {		if filename == "ignored" {			return fmt.Errorf("should not walk into ignored")		}		if strings.HasPrefix(filename, "ignored_paths") {			return fmt.Errorf("should not walk into ignored_paths")		}		return nil	})	if err != nil {		t.Errorf("failed to walk: %s", err.Error())		return	}	// check assets	assets, err := originDecoder.Assets()	if err != nil {		t.Errorf("failed to get assets: %s", err.Error())		return	}	if assets["test.svg"] == nil {		t.Errorf("should have test.svg asset, got %v", assets)		return	}	packager := packager.NewPackager(originDecoder)	// pack	zip, err := packager.Pack(52428800)	if err != nil {		t.Errorf("failed to pack: %s", err.Error())		return	}	// sign	signed, err := signer.SignPlugin(zip)	if err != nil {		t.Errorf("failed to sign: %s", err.Error())		return	}	signedDecoder, err := decoder.NewZipPluginDecoder(signed)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// check assets	assets, err = signedDecoder.Assets()	if err != nil {		t.Errorf("failed to get assets: %s", err.Error())		return	}	if assets["test.svg"] == nil {		t.Errorf("should have test.svg asset, got %v", assets)		return	}	// verify	err = decoder.VerifyPlugin(signedDecoder)	if err != nil {		t.Errorf("failed to verify: %s", err.Error())		return	}}func TestWrongSign(t *testing.T) {	// create a minimal test plugin	zip := createMinimalPlugin(t)	if zip == nil {		return	}	// sign	signed, err := signer.SignPlugin(zip)	if err != nil {		t.Errorf("failed to sign: %s", err.Error())		return	}	// modify the signed file, signature is at the end of the file	signed[len(signed)-1] = 0	signed[len(signed)-2] = 0	// create a new decoder	signedDecoder, err := decoder.NewZipPluginDecoder(signed)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// verify (expected to fail)	err = decoder.VerifyPlugin(signedDecoder)	if err == nil {		t.Errorf("should fail to verify")		return	}}// loadPublicKeyFile loads a key file from the embed.FS and returns the public keyfunc loadPublicKeyFile(t *testing.T, keyFile string) *rsa.PublicKey {	keyBytes, err := keys.ReadFile(filepath.Join("testdata/keys", keyFile))	if err != nil {		t.Fatalf("failed to read key file: %s", err.Error())	}	key, err := encryption.LoadPublicKey(keyBytes)	if err != nil {		t.Fatalf("failed to load public key: %s", err.Error())	}	return key}// loadPrivateKeyFile loads a key file from the embed.FS and returns the private keyfunc loadPrivateKeyFile(t *testing.T, keyFile string) *rsa.PrivateKey {	keyBytes, err := keys.ReadFile(filepath.Join("testdata/keys", keyFile))	if err != nil {		t.Fatalf("failed to read key file: %s", err.Error())	}	key, err := encryption.LoadPrivateKey(keyBytes)	if err != nil {		t.Fatalf("failed to load private key: %s", err.Error())	}	return key}// extractPublicKey extracts the key file from the embed.FS and returns the file pathfunc extractKeyFile(t *testing.T, keyFile string, tmpDir string) string {	keyBytes, err := keys.ReadFile(filepath.Join("testdata/keys", keyFile))	if err != nil {		t.Fatalf("failed to read key file: %s", err.Error())	}	keyPath := filepath.Join(tmpDir, keyFile)	if err := os.WriteFile(keyPath, keyBytes, 0644); err != nil {		t.Fatalf("failed to write key file: %s", err.Error())	}	return keyPath}func TestSignPluginWithPrivateKey(t *testing.T) {	// load public keys from embed.FS	publicKey1 := loadPublicKeyFile(t, "test_key_pair_1.public.pem")	publicKey2 := loadPublicKeyFile(t, "test_key_pair_2.public.pem")	// load private keys from embed.FS	privateKey1 := loadPrivateKeyFile(t, "test_key_pair_1.private.pem")	privateKey2 := loadPrivateKeyFile(t, "test_key_pair_2.private.pem")	// create a minimal test plugin	zip := createMinimalPlugin(t)	if zip == nil {		return	}	// sign with private key 1 and create decoder	signed1, err := withkey.SignPluginWithPrivateKey(zip, privateKey1)	if err != nil {		t.Errorf("failed to sign with private key 1: %s", err.Error())		return	}	signedDecoder1, err := decoder.NewZipPluginDecoder(signed1)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// sign with private key 2 and create decoder	signed2, err := withkey.SignPluginWithPrivateKey(zip, privateKey2)	if err != nil {		t.Errorf("failed to sign with private key 2: %s", err.Error())		return	}	signedDecoder2, err := decoder.NewZipPluginDecoder(signed2)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// tamper the signed1 file and create decoder	modifiedSigned1 := make([]byte, len(signed1))	copy(modifiedSigned1, signed1)	modifiedSigned1[len(modifiedSigned1)-10] = 0	modifiedDecoder1, err := decoder.NewZipPluginDecoder(modifiedSigned1)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// define test cases	tests := []struct {		name          string		signedDecoder decoder.PluginDecoder		publicKeys    []*rsa.PublicKey		expectSuccess bool	}{		{			name:          "verify plugin signed with private key 1 using embedded public key (should fail)",			signedDecoder: signedDecoder1,			publicKeys:    nil, // use embedded public key			expectSuccess: false,		},		{			name:          "verify plugin signed with private key 1 using public key 1 (should succeed)",			signedDecoder: signedDecoder1,			publicKeys:    []*rsa.PublicKey{publicKey1},			expectSuccess: true,		},		{			name:          "verify plugin signed with private key 1 using public key 2 (should fail)",			signedDecoder: signedDecoder1,			publicKeys:    []*rsa.PublicKey{publicKey2},			expectSuccess: false,		},		{			name:          "verify plugin signed with private key 2 using public key 1 (should fail)",			signedDecoder: signedDecoder2,			publicKeys:    []*rsa.PublicKey{publicKey1},			expectSuccess: false,		},		{			name:          "verify plugin signed with private key 2 using public key 2 (should succeed)",			signedDecoder: signedDecoder2,			publicKeys:    []*rsa.PublicKey{publicKey2},			expectSuccess: true,		},		{			name:          "verify modified plugin signed with private key 1 using public key 1 (should fail)",			signedDecoder: modifiedDecoder1,			publicKeys:    []*rsa.PublicKey{publicKey1},			expectSuccess: false,		},		{			name:          "verify modified plugin signed with private key 1 using public key 2 (should fail)",			signedDecoder: modifiedDecoder1,			publicKeys:    []*rsa.PublicKey{publicKey2},			expectSuccess: false,		},	}	// run test cases	for _, tt := range tests {		t.Run(tt.name, func(t *testing.T) {			var err error			if tt.publicKeys == nil {				err = decoder.VerifyPlugin(tt.signedDecoder)			} else {				err = decoder.VerifyPluginWithPublicKeys(tt.signedDecoder, tt.publicKeys)			}			if tt.expectSuccess && err != nil {				t.Errorf("expected success but got error: %s", err.Error())			}			if !tt.expectSuccess && err == nil {				t.Errorf("expected failure but got success")			}		})	}}func TestVerifyPluginWithThirdPartyKeys(t *testing.T) {	// create a temporary directory for the public key files (needed for storing the paths in environment variable)	tempDir := t.TempDir()	// extract public keys to files from embed.FS (needed for storing the paths in environment variable)	publicKey1Path := extractKeyFile(t, "test_key_pair_1.public.pem", tempDir)	publicKey2Path := extractKeyFile(t, "test_key_pair_2.public.pem", tempDir)	// load private keys from embed.FS	privateKey1 := loadPrivateKeyFile(t, "test_key_pair_1.private.pem")	privateKey2 := loadPrivateKeyFile(t, "test_key_pair_2.private.pem")	// create a minimal test plugin	zip := createMinimalPlugin(t)	if zip == nil {		return	}	// sign with private key 1 and create decoder	signed1, err := withkey.SignPluginWithPrivateKey(zip, privateKey1)	if err != nil {		t.Errorf("failed to sign with private key 1: %s", err.Error())		return	}	signedDecoder1, err := decoder.NewZipPluginDecoder(signed1)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// sign with private key 2 and create decoder	signed2, err := withkey.SignPluginWithPrivateKey(zip, privateKey2)	if err != nil {		t.Errorf("failed to sign with private key 2: %s", err.Error())		return	}	signedDecoder2, err := decoder.NewZipPluginDecoder(signed2)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// tamper the signed1 file and create decoder	modifiedSigned1 := make([]byte, len(signed1))	copy(modifiedSigned1, signed1)	modifiedSigned1[len(modifiedSigned1)-10] = 0	modifiedDecoder1, err := decoder.NewZipPluginDecoder(modifiedSigned1)	if err != nil {		t.Errorf("failed to create zip decoder: %s", err.Error())		return	}	// define test cases	tests := []struct {		name          string		keyPaths      string		signedDecoder decoder.PluginDecoder		expectSuccess bool	}{		{			name:          "third-party verification with public key 1 (should succeed)",			keyPaths:      publicKey1Path,			signedDecoder: signedDecoder1,			expectSuccess: true,		},		{			name:          "third-party verification with public key 2 (should fail)",			keyPaths:      publicKey2Path,			signedDecoder: signedDecoder1,			expectSuccess: false,		},		{			name:          "third-party verification with both keys (should succeed)",			keyPaths:      fmt.Sprintf("%s,%s", publicKey1Path, publicKey2Path),			signedDecoder: signedDecoder1,			expectSuccess: true,		},		{			name:          "third-party verification with empty key path (should fail)",			keyPaths:      "",			signedDecoder: signedDecoder1,			expectSuccess: false,		},		{			name:          "third-party verification with non-existent key path (should fail)",			keyPaths:      "/non/existent/path.pem",			signedDecoder: signedDecoder1,			expectSuccess: false,		},		{			name:          "third-party verification with multiple keys including non-existent path (should fail)",			keyPaths:      fmt.Sprintf("%s,%s,/non/existent/path.pem", publicKey1Path, publicKey2Path),			signedDecoder: signedDecoder1,			expectSuccess: false,		},		{			name:          "third-party verification with multiple keys including extra spaces (should succeed)",			keyPaths:      fmt.Sprintf(" %s , %s ", publicKey1Path, publicKey2Path),			signedDecoder: signedDecoder1,			expectSuccess: true,		},		{			name:          "third-party verification with both keys, for file signed with key 2 (should succeed)",			keyPaths:      fmt.Sprintf("%s,%s", publicKey1Path, publicKey2Path),			signedDecoder: signedDecoder2,			expectSuccess: true,		},		{			name:          "third-party verification with both keys, for modified file (should fail)",			keyPaths:      fmt.Sprintf("%s,%s", publicKey1Path, publicKey2Path),			signedDecoder: modifiedDecoder1,			expectSuccess: false,		},	}	// run test cases	for _, tt := range tests {		t.Run(tt.name, func(t *testing.T) {			err := decoder.VerifyPluginWithPublicKeyPaths(tt.signedDecoder, strings.Split(tt.keyPaths, ","))			if tt.expectSuccess && err != nil {				t.Errorf("expected success but got error: %s", err.Error())			}			if !tt.expectSuccess && err == nil {				t.Errorf("expected failure but got success")			}		})	}}
 |