packager_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. package plugin_packager
  2. import (
  3. "crypto/rsa"
  4. "embed"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. "testing"
  10. "github.com/langgenius/dify-plugin-daemon/internal/utils/encryption"
  11. "github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/decoder"
  12. "github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/packager"
  13. "github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/signer"
  14. "github.com/langgenius/dify-plugin-daemon/pkg/plugin_packager/signer/withkey"
  15. )
  16. //go:embed testdata/manifest.yaml
  17. var manifest []byte
  18. //go:embed testdata/neko.yaml
  19. var neko []byte
  20. //go:embed testdata/.difyignore
  21. var dify_ignore []byte
  22. //go:embed testdata/ignored
  23. var ignored []byte
  24. //go:embed testdata/_assets/test.svg
  25. var test_svg []byte
  26. //go:embed testdata/keys
  27. var keys embed.FS
  28. // createMinimalPlugin creates a minimal test plugin and returns the zip file
  29. func createMinimalPlugin(t *testing.T) []byte {
  30. // create a temp directory
  31. tempDir := t.TempDir()
  32. // create basic files
  33. if err := os.WriteFile(filepath.Join(tempDir, "manifest.yaml"), manifest, 0644); err != nil {
  34. t.Errorf("failed to write manifest: %s", err.Error())
  35. return nil
  36. }
  37. if err := os.WriteFile(filepath.Join(tempDir, "neko.yaml"), neko, 0644); err != nil {
  38. t.Errorf("failed to write neko: %s", err.Error())
  39. return nil
  40. }
  41. // create _assets directory and files
  42. if err := os.MkdirAll(filepath.Join(tempDir, "_assets"), 0755); err != nil {
  43. t.Errorf("failed to create _assets directory: %s", err.Error())
  44. return nil
  45. }
  46. if err := os.WriteFile(filepath.Join(tempDir, "_assets/test.svg"), test_svg, 0644); err != nil {
  47. t.Errorf("failed to write test.svg: %s", err.Error())
  48. return nil
  49. }
  50. // create decoder
  51. originDecoder, err := decoder.NewFSPluginDecoder(tempDir)
  52. if err != nil {
  53. t.Errorf("failed to create decoder: %s", err.Error())
  54. return nil
  55. }
  56. // create packager
  57. packager := packager.NewPackager(originDecoder)
  58. // pack
  59. zip, err := packager.Pack(52428800)
  60. if err != nil {
  61. t.Errorf("failed to pack: %s", err.Error())
  62. return nil
  63. }
  64. return zip
  65. }
  66. func TestPackagerAndVerifier(t *testing.T) {
  67. // create a temp directory
  68. tempDir := t.TempDir()
  69. // create manifest
  70. if err := os.WriteFile(filepath.Join(tempDir, "manifest.yaml"), manifest, 0644); err != nil {
  71. t.Errorf("failed to write manifest: %s", err.Error())
  72. return
  73. }
  74. if err := os.WriteFile(filepath.Join(tempDir, "neko.yaml"), neko, 0644); err != nil {
  75. t.Errorf("failed to write neko: %s", err.Error())
  76. return
  77. }
  78. // create .difyignore
  79. if err := os.WriteFile(filepath.Join(tempDir, ".difyignore"), dify_ignore, 0644); err != nil {
  80. t.Errorf("failed to write .difyignore: %s", err.Error())
  81. return
  82. }
  83. // create ignored
  84. if err := os.WriteFile(filepath.Join(tempDir, "ignored"), ignored, 0644); err != nil {
  85. t.Errorf("failed to write ignored: %s", err.Error())
  86. return
  87. }
  88. // create ignored_paths
  89. if err := os.MkdirAll(filepath.Join(tempDir, "ignored_paths"), 0755); err != nil {
  90. t.Errorf("failed to create ignored_paths directory: %s", err.Error())
  91. return
  92. }
  93. // create ignored_paths/ignored
  94. if err := os.WriteFile(filepath.Join(tempDir, "ignored_paths/ignored"), ignored, 0644); err != nil {
  95. t.Errorf("failed to write ignored_paths/ignored: %s", err.Error())
  96. return
  97. }
  98. if err := os.MkdirAll(filepath.Join(tempDir, "_assets"), 0755); err != nil {
  99. t.Errorf("failed to create _assets directory: %s", err.Error())
  100. return
  101. }
  102. if err := os.WriteFile(filepath.Join(tempDir, "_assets/test.svg"), test_svg, 0644); err != nil {
  103. t.Errorf("failed to write test.svg: %s", err.Error())
  104. return
  105. }
  106. originDecoder, err := decoder.NewFSPluginDecoder(tempDir)
  107. if err != nil {
  108. t.Errorf("failed to create decoder: %s", err.Error())
  109. return
  110. }
  111. // walk
  112. err = originDecoder.Walk(func(filename string, dir string) error {
  113. if filename == "ignored" {
  114. return fmt.Errorf("should not walk into ignored")
  115. }
  116. if strings.HasPrefix(filename, "ignored_paths") {
  117. return fmt.Errorf("should not walk into ignored_paths")
  118. }
  119. return nil
  120. })
  121. if err != nil {
  122. t.Errorf("failed to walk: %s", err.Error())
  123. return
  124. }
  125. // check assets
  126. assets, err := originDecoder.Assets()
  127. if err != nil {
  128. t.Errorf("failed to get assets: %s", err.Error())
  129. return
  130. }
  131. if assets["test.svg"] == nil {
  132. t.Errorf("should have test.svg asset, got %v", assets)
  133. return
  134. }
  135. packager := packager.NewPackager(originDecoder)
  136. // pack
  137. zip, err := packager.Pack(52428800)
  138. if err != nil {
  139. t.Errorf("failed to pack: %s", err.Error())
  140. return
  141. }
  142. // sign
  143. signed, err := signer.SignPlugin(zip)
  144. if err != nil {
  145. t.Errorf("failed to sign: %s", err.Error())
  146. return
  147. }
  148. signedDecoder, err := decoder.NewZipPluginDecoder(signed)
  149. if err != nil {
  150. t.Errorf("failed to create zip decoder: %s", err.Error())
  151. return
  152. }
  153. // check assets
  154. assets, err = signedDecoder.Assets()
  155. if err != nil {
  156. t.Errorf("failed to get assets: %s", err.Error())
  157. return
  158. }
  159. if assets["test.svg"] == nil {
  160. t.Errorf("should have test.svg asset, got %v", assets)
  161. return
  162. }
  163. // verify
  164. err = decoder.VerifyPlugin(signedDecoder)
  165. if err != nil {
  166. t.Errorf("failed to verify: %s", err.Error())
  167. return
  168. }
  169. }
  170. func TestWrongSign(t *testing.T) {
  171. // create a minimal test plugin
  172. zip := createMinimalPlugin(t)
  173. if zip == nil {
  174. return
  175. }
  176. // sign
  177. signed, err := signer.SignPlugin(zip)
  178. if err != nil {
  179. t.Errorf("failed to sign: %s", err.Error())
  180. return
  181. }
  182. // modify the signed file, signature is at the end of the file
  183. signed[len(signed)-1] = 0
  184. signed[len(signed)-2] = 0
  185. // create a new decoder
  186. signedDecoder, err := decoder.NewZipPluginDecoder(signed)
  187. if err != nil {
  188. t.Errorf("failed to create zip decoder: %s", err.Error())
  189. return
  190. }
  191. // verify (expected to fail)
  192. err = decoder.VerifyPlugin(signedDecoder)
  193. if err == nil {
  194. t.Errorf("should fail to verify")
  195. return
  196. }
  197. }
  198. // loadPublicKeyFile loads a key file from the embed.FS and returns the public key
  199. func loadPublicKeyFile(t *testing.T, keyFile string) *rsa.PublicKey {
  200. keyBytes, err := keys.ReadFile(filepath.Join("testdata/keys", keyFile))
  201. if err != nil {
  202. t.Fatalf("failed to read key file: %s", err.Error())
  203. }
  204. key, err := encryption.LoadPublicKey(keyBytes)
  205. if err != nil {
  206. t.Fatalf("failed to load public key: %s", err.Error())
  207. }
  208. return key
  209. }
  210. // loadPrivateKeyFile loads a key file from the embed.FS and returns the private key
  211. func loadPrivateKeyFile(t *testing.T, keyFile string) *rsa.PrivateKey {
  212. keyBytes, err := keys.ReadFile(filepath.Join("testdata/keys", keyFile))
  213. if err != nil {
  214. t.Fatalf("failed to read key file: %s", err.Error())
  215. }
  216. key, err := encryption.LoadPrivateKey(keyBytes)
  217. if err != nil {
  218. t.Fatalf("failed to load private key: %s", err.Error())
  219. }
  220. return key
  221. }
  222. // extractPublicKey extracts the key file from the embed.FS and returns the file path
  223. func extractKeyFile(t *testing.T, keyFile string, tmpDir string) string {
  224. keyBytes, err := keys.ReadFile(filepath.Join("testdata/keys", keyFile))
  225. if err != nil {
  226. t.Fatalf("failed to read key file: %s", err.Error())
  227. }
  228. keyPath := filepath.Join(tmpDir, keyFile)
  229. if err := os.WriteFile(keyPath, keyBytes, 0644); err != nil {
  230. t.Fatalf("failed to write key file: %s", err.Error())
  231. }
  232. return keyPath
  233. }
  234. func TestSignPluginWithPrivateKey(t *testing.T) {
  235. // load public keys from embed.FS
  236. publicKey1 := loadPublicKeyFile(t, "test_key_pair_1.public.pem")
  237. publicKey2 := loadPublicKeyFile(t, "test_key_pair_2.public.pem")
  238. // load private keys from embed.FS
  239. privateKey1 := loadPrivateKeyFile(t, "test_key_pair_1.private.pem")
  240. privateKey2 := loadPrivateKeyFile(t, "test_key_pair_2.private.pem")
  241. // create a minimal test plugin
  242. zip := createMinimalPlugin(t)
  243. if zip == nil {
  244. return
  245. }
  246. // sign with private key 1 and create decoder
  247. signed1, err := withkey.SignPluginWithPrivateKey(zip, privateKey1)
  248. if err != nil {
  249. t.Errorf("failed to sign with private key 1: %s", err.Error())
  250. return
  251. }
  252. signedDecoder1, err := decoder.NewZipPluginDecoder(signed1)
  253. if err != nil {
  254. t.Errorf("failed to create zip decoder: %s", err.Error())
  255. return
  256. }
  257. // sign with private key 2 and create decoder
  258. signed2, err := withkey.SignPluginWithPrivateKey(zip, privateKey2)
  259. if err != nil {
  260. t.Errorf("failed to sign with private key 2: %s", err.Error())
  261. return
  262. }
  263. signedDecoder2, err := decoder.NewZipPluginDecoder(signed2)
  264. if err != nil {
  265. t.Errorf("failed to create zip decoder: %s", err.Error())
  266. return
  267. }
  268. // tamper the signed1 file and create decoder
  269. modifiedSigned1 := make([]byte, len(signed1))
  270. copy(modifiedSigned1, signed1)
  271. modifiedSigned1[len(modifiedSigned1)-10] = 0
  272. modifiedDecoder1, err := decoder.NewZipPluginDecoder(modifiedSigned1)
  273. if err != nil {
  274. t.Errorf("failed to create zip decoder: %s", err.Error())
  275. return
  276. }
  277. // define test cases
  278. tests := []struct {
  279. name string
  280. signedDecoder decoder.PluginDecoder
  281. publicKeys []*rsa.PublicKey
  282. expectSuccess bool
  283. }{
  284. {
  285. name: "verify plugin signed with private key 1 using embedded public key (should fail)",
  286. signedDecoder: signedDecoder1,
  287. publicKeys: nil, // use embedded public key
  288. expectSuccess: false,
  289. },
  290. {
  291. name: "verify plugin signed with private key 1 using public key 1 (should succeed)",
  292. signedDecoder: signedDecoder1,
  293. publicKeys: []*rsa.PublicKey{publicKey1},
  294. expectSuccess: true,
  295. },
  296. {
  297. name: "verify plugin signed with private key 1 using public key 2 (should fail)",
  298. signedDecoder: signedDecoder1,
  299. publicKeys: []*rsa.PublicKey{publicKey2},
  300. expectSuccess: false,
  301. },
  302. {
  303. name: "verify plugin signed with private key 2 using public key 1 (should fail)",
  304. signedDecoder: signedDecoder2,
  305. publicKeys: []*rsa.PublicKey{publicKey1},
  306. expectSuccess: false,
  307. },
  308. {
  309. name: "verify plugin signed with private key 2 using public key 2 (should succeed)",
  310. signedDecoder: signedDecoder2,
  311. publicKeys: []*rsa.PublicKey{publicKey2},
  312. expectSuccess: true,
  313. },
  314. {
  315. name: "verify modified plugin signed with private key 1 using public key 1 (should fail)",
  316. signedDecoder: modifiedDecoder1,
  317. publicKeys: []*rsa.PublicKey{publicKey1},
  318. expectSuccess: false,
  319. },
  320. {
  321. name: "verify modified plugin signed with private key 1 using public key 2 (should fail)",
  322. signedDecoder: modifiedDecoder1,
  323. publicKeys: []*rsa.PublicKey{publicKey2},
  324. expectSuccess: false,
  325. },
  326. }
  327. // run test cases
  328. for _, tt := range tests {
  329. t.Run(tt.name, func(t *testing.T) {
  330. var err error
  331. if tt.publicKeys == nil {
  332. err = decoder.VerifyPlugin(tt.signedDecoder)
  333. } else {
  334. err = decoder.VerifyPluginWithPublicKeys(tt.signedDecoder, tt.publicKeys)
  335. }
  336. if tt.expectSuccess && err != nil {
  337. t.Errorf("expected success but got error: %s", err.Error())
  338. }
  339. if !tt.expectSuccess && err == nil {
  340. t.Errorf("expected failure but got success")
  341. }
  342. })
  343. }
  344. }
  345. func TestVerifyPluginWithThirdPartyKeys(t *testing.T) {
  346. // create a temporary directory for the public key files (needed for storing the paths in environment variable)
  347. tempDir := t.TempDir()
  348. // extract public keys to files from embed.FS (needed for storing the paths in environment variable)
  349. publicKey1Path := extractKeyFile(t, "test_key_pair_1.public.pem", tempDir)
  350. publicKey2Path := extractKeyFile(t, "test_key_pair_2.public.pem", tempDir)
  351. // load private keys from embed.FS
  352. privateKey1 := loadPrivateKeyFile(t, "test_key_pair_1.private.pem")
  353. privateKey2 := loadPrivateKeyFile(t, "test_key_pair_2.private.pem")
  354. // create a minimal test plugin
  355. zip := createMinimalPlugin(t)
  356. if zip == nil {
  357. return
  358. }
  359. // sign with private key 1 and create decoder
  360. signed1, err := withkey.SignPluginWithPrivateKey(zip, privateKey1)
  361. if err != nil {
  362. t.Errorf("failed to sign with private key 1: %s", err.Error())
  363. return
  364. }
  365. signedDecoder1, err := decoder.NewZipPluginDecoder(signed1)
  366. if err != nil {
  367. t.Errorf("failed to create zip decoder: %s", err.Error())
  368. return
  369. }
  370. // sign with private key 2 and create decoder
  371. signed2, err := withkey.SignPluginWithPrivateKey(zip, privateKey2)
  372. if err != nil {
  373. t.Errorf("failed to sign with private key 2: %s", err.Error())
  374. return
  375. }
  376. signedDecoder2, err := decoder.NewZipPluginDecoder(signed2)
  377. if err != nil {
  378. t.Errorf("failed to create zip decoder: %s", err.Error())
  379. return
  380. }
  381. // tamper the signed1 file and create decoder
  382. modifiedSigned1 := make([]byte, len(signed1))
  383. copy(modifiedSigned1, signed1)
  384. modifiedSigned1[len(modifiedSigned1)-10] = 0
  385. modifiedDecoder1, err := decoder.NewZipPluginDecoder(modifiedSigned1)
  386. if err != nil {
  387. t.Errorf("failed to create zip decoder: %s", err.Error())
  388. return
  389. }
  390. // define test cases
  391. tests := []struct {
  392. name string
  393. keyPaths string
  394. signedDecoder decoder.PluginDecoder
  395. expectSuccess bool
  396. }{
  397. {
  398. name: "third-party verification with public key 1 (should succeed)",
  399. keyPaths: publicKey1Path,
  400. signedDecoder: signedDecoder1,
  401. expectSuccess: true,
  402. },
  403. {
  404. name: "third-party verification with public key 2 (should fail)",
  405. keyPaths: publicKey2Path,
  406. signedDecoder: signedDecoder1,
  407. expectSuccess: false,
  408. },
  409. {
  410. name: "third-party verification with both keys (should succeed)",
  411. keyPaths: fmt.Sprintf("%s,%s", publicKey1Path, publicKey2Path),
  412. signedDecoder: signedDecoder1,
  413. expectSuccess: true,
  414. },
  415. {
  416. name: "third-party verification with empty key path (should fail)",
  417. keyPaths: "",
  418. signedDecoder: signedDecoder1,
  419. expectSuccess: false,
  420. },
  421. {
  422. name: "third-party verification with non-existent key path (should fail)",
  423. keyPaths: "/non/existent/path.pem",
  424. signedDecoder: signedDecoder1,
  425. expectSuccess: false,
  426. },
  427. {
  428. name: "third-party verification with multiple keys including non-existent path (should fail)",
  429. keyPaths: fmt.Sprintf("%s,%s,/non/existent/path.pem", publicKey1Path, publicKey2Path),
  430. signedDecoder: signedDecoder1,
  431. expectSuccess: false,
  432. },
  433. {
  434. name: "third-party verification with multiple keys including extra spaces (should succeed)",
  435. keyPaths: fmt.Sprintf(" %s , %s ", publicKey1Path, publicKey2Path),
  436. signedDecoder: signedDecoder1,
  437. expectSuccess: true,
  438. },
  439. {
  440. name: "third-party verification with both keys, for file signed with key 2 (should succeed)",
  441. keyPaths: fmt.Sprintf("%s,%s", publicKey1Path, publicKey2Path),
  442. signedDecoder: signedDecoder2,
  443. expectSuccess: true,
  444. },
  445. {
  446. name: "third-party verification with both keys, for modified file (should fail)",
  447. keyPaths: fmt.Sprintf("%s,%s", publicKey1Path, publicKey2Path),
  448. signedDecoder: modifiedDecoder1,
  449. expectSuccess: false,
  450. },
  451. }
  452. // run test cases
  453. for _, tt := range tests {
  454. t.Run(tt.name, func(t *testing.T) {
  455. err := decoder.VerifyPluginWithPublicKeyPaths(tt.signedDecoder, strings.Split(tt.keyPaths, ","))
  456. if tt.expectSuccess && err != nil {
  457. t.Errorf("expected success but got error: %s", err.Error())
  458. }
  459. if !tt.expectSuccess && err == nil {
  460. t.Errorf("expected failure but got success")
  461. }
  462. })
  463. }
  464. }