github.com/Unheilbar/quorum@v1.0.0/plugin/utils_test.go (about) 1 package plugin 2 3 import ( 4 "archive/zip" 5 "io/ioutil" 6 "os" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 ) 11 12 func TestIsValidTargetURL(t *testing.T) { 13 assert.Error(t, isValidTargetURL("https://localhost.com", "http://localhost.com")) 14 assert.Error(t, isValidTargetURL("https://localhost", "http://localhost.com")) 15 16 if err := isValidTargetURL("http://localhost.com", "http://localhost.com"); err != nil { 17 t.Errorf(err.Error()) 18 } 19 20 if err := isValidTargetURL("https://localhost.com/../../", "https://localhost.com"); err != nil { 21 t.Errorf(err.Error()) 22 } 23 } 24 25 func TestIsCleanFileName(t *testing.T) { 26 assert.True(t, isCleanFileName("filename"), "filename is not valid") 27 assert.True(t, isCleanFileName("filename.exe"), "filename with .exe") 28 29 assert.False(t, isCleanFileName(""), "filename is not valid") 30 assert.False(t, isCleanFileName("filename/"), "filename with /") 31 assert.False(t, isCleanFileName("filename\\u00"), "filename with \\") 32 assert.False(t, isCleanFileName("filename$"), "filename with $") 33 assert.False(t, isCleanFileName("filename%"), "filename with %") 34 assert.False(t, isCleanFileName("filename%00"), "filename with %") 35 } 36 37 func TestIsCleanEntryPoint(t *testing.T) { 38 assert.True(t, isCleanEntryPoint("entrypoint"), "entrypoint is not valid") 39 assert.True(t, isCleanEntryPoint("entrypoint.exe"), "entrypoint with .exe") 40 41 assert.False(t, isCleanEntryPoint(""), "entrypoint is not valid") 42 assert.False(t, isCleanEntryPoint("entrypoint/"), "entrypoint with /") 43 assert.False(t, isCleanEntryPoint("entrypoint\\u00"), "entrypoint with \\") 44 assert.False(t, isCleanEntryPoint("entrypoint$"), "entrypoint with $") 45 assert.False(t, isCleanEntryPoint("entrypoint%"), "entrypoint with %") 46 assert.False(t, isCleanEntryPoint("entrypoint%00"), "entrypoint with %") 47 } 48 49 func TestResolveFilePath_whenTypical(t *testing.T) { 50 tmpDir, err := ioutil.TempDir("", "q-") 51 if err != nil { 52 t.Fatal(err) 53 } 54 defer func() { 55 _ = os.RemoveAll(tmpDir) 56 }() 57 f, err := ioutil.TempFile(tmpDir, "f-") 58 if err != nil { 59 t.Fatal(err) 60 } 61 actualFile, err := resolveFilePath("file://" + f.Name()) 62 63 assert.NoError(t, err) 64 assert.Equal(t, f.Name(), actualFile) 65 } 66 67 func TestResolveFilePath_whenInvalidFileURI(t *testing.T) { 68 _, err := resolveFilePath("://arbitrary non uri") 69 70 assert.Error(t, err) 71 } 72 73 func TestVerify_whenTypicalWithBintraySigner(t *testing.T) { 74 err := verify(validSignatureSignedByBintray, bintrayPublicKey, arbitrarySHA256checksum) 75 76 assert.NoError(t, err) 77 } 78 79 func TestVerify_whenTypicalWithStandardSigner(t *testing.T) { 80 err := verify(validSignature, signerPubKey, arbitraryChecksum) 81 82 assert.NoError(t, err) 83 } 84 85 func TestVerify_whenInvalid(t *testing.T) { 86 err := verify(validSignature, arbitraryPubKey, arbitraryChecksum) 87 88 assert.Error(t, err) 89 } 90 91 func TestUnpackPlugin_whenTypical(t *testing.T) { 92 tmpDir, err := ioutil.TempDir("", "q-") 93 if err != nil { 94 t.Fatal(err) 95 } 96 defer func() { 97 _ = os.RemoveAll(tmpDir) 98 }() 99 tmpZipFile, err := createArbitraryZip(tmpDir) 100 if err != nil { 101 t.Fatal(err) 102 } 103 104 workspace, meta, err := unpackPlugin(tmpZipFile) 105 106 if err != nil { 107 t.Fatal(err) 108 } 109 defer func() { 110 _ = os.RemoveAll(workspace) 111 }() 112 assert.NotEmpty(t, workspace) 113 assert.NotNil(t, meta) 114 } 115 116 func createArbitraryZip(tmpDir string) (string, error) { 117 tmpFile, err := ioutil.TempFile(tmpDir, "f-") 118 if err != nil { 119 return "", err 120 } 121 122 // Create a new zip archive. 123 w := zip.NewWriter(tmpFile) 124 defer func() { 125 _ = w.Close() 126 }() 127 128 // Add some files to the archive. 129 var files = []struct { 130 Name, Body string 131 }{ 132 {"readme.txt", "This archive contains some text files."}, 133 {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"}, 134 {"plugin-meta.json", ` 135 { 136 "name": "arbitrary-plugin", 137 "version": "1.0.0", 138 "entrypoint": "echo", 139 "parameters": [ 140 "hello world" 141 ] 142 } 143 `}, 144 } 145 for _, file := range files { 146 f, err := w.Create(file.Name) 147 if err != nil { 148 return "", err 149 } 150 _, err = f.Write([]byte(file.Body)) 151 if err != nil { 152 return "", err 153 } 154 } 155 156 return tmpFile.Name(), nil 157 } 158 159 var ( 160 arbitraryChecksum = "bf9a942afca462a9fb45f471f8d4db8c79cf332d" 161 arbitrarySHA256checksum = "697dc791f0df55fbb86a7d985d29f23feff69e41681816a2c17352dcf10e693d" 162 // signature of the signed arbitrarySHA256checksum 163 validSignatureSignedByBintray = []byte(` 164 -----BEGIN PGP SIGNATURE----- 165 Version: BCPG v1.53 166 167 iQIcBAABCAAGBQJeZ7h7AAoJEDec4ZLUAathdxYP/jKPYxFlWiI0520SU5zFTfx4 168 F6fQL4d0uGsg/xlxDQbiYP+3aNAMuPzmDAJtu0qn8HnG51uSBJ95YWjgvivE2sw3 169 xVK9vsAEjkQRa3yMBgBCrtlfyaYz/URbzEiVU8BGUFusnohx1kh6Ak7SO8S7bsbk 170 LIiKtcVs5RqhTwQBOu8SP4pROeRlbLjJ99WLUjKl8l8Vy753ov0J8ohsFIOGgiou 171 8UAHAnqxYuUwkZ8hPLUzdL1GxR4zo9XK6ll1XayTDjVrKsM2MFM9lbLgeXnb26pj 172 VY9M3WixwaSS5bZOqwNYJYV4YVnIOiS9gplvyPI4joRjgXWVRgm1KAoZ20JCJ5sn 173 SILRjaUYuNk/rHxPeVtNTTO5GD9iroroj5DKLh7H9qZMwZ/3/d+3rFlzFicS18cH 174 kItSO0raRfMD7PT6+m6q/Ss/Ssx8TBbbKE7IbSPNoab13VYPLx2pN0Z0gARozTe3 175 1yrtPmqUyJw/R96UXWjQLIANXHkMi5X56az0RUK68ALMROGchptoXEAdOGLyx/lK 176 CbdmcD4bESihwjGJvMtFNqQaLAkAyYH8BJ2xSx0/DDJYnazMevxCLaJTgop9pCaf 177 i8wSXeyp0KquNgi7gWUOizVrE/Rzg0w3xCOPvwduwcLHFtdkGnG0rceU7axy8jf6 178 CX+P2tVLyv6EMv0ej8Wm 179 =+Qxa 180 -----END PGP SIGNATURE----- 181 `) 182 bintrayPublicKey = []byte(` 183 -----BEGIN PGP PUBLIC KEY BLOCK----- 184 185 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 231 JESa2SNRfw== 232 =EI0Z 233 -----END PGP PUBLIC KEY BLOCK----- 234 `) 235 // signature of the signed arbitraryChecksum 236 validSignature = []byte(` 237 -----BEGIN PGP SIGNATURE----- 238 239 iQJBBAEBCAArFiEEHGpboPTpUoYceZX2PIgUS38YTSgFAl1C/8ENHGFiY0B0ZXN0 240 LmNvbQAKCRA8iBRLfxhNKHeBEACs14x1+UoVEVNVDNSJORsQy6nthHiwrb5l66dW 241 KPcEt96y7KXJObSF7TWfmGjIgQXmDnrwMY78bKcbWVK90siDwA0SajUwmwmCbCeC 242 nMTIza1a64KblJRVGal9D5EWLdAOuQkAV2tddyWMqdvv2ef46y+2zmoKE3bOQLXj 243 sCi5e8myuh5ottfrf5Tkxi7QHrWICxYjAMEUkvke/jbYUFi1787VnHZ8LDG1x5WN 244 yz3KysyaraMiOstk5PcACU+bsvEIXFppJsgx9eNqdyfQ0/oMzKlqlHhss/W5osyq 245 LeVY9dcMXUSNGmB6deJde93pv3kYnLarhEM5Ovm5BxYMyzudk9hUy3wXyb51EPaL 246 z/hYViGpBVSwKY6q47s8duXruOA0TzYu5jYmJd+CzqBkDbJfh7JG9iJkdG4Q30ui 247 D2wvTBJfz6wu1qYj0semX4l4ntpJ6OcIvD0BpP1wz3eC9rt+3RzrjVWPbVoTOyB0 248 V7vVQPMJowoPvluIUP0eInc+jDue2Z/8DHjWDu1k4jmZbO7r/5Hib79JtA3LIGqq 249 CizH/cFWXLJAh6n7tFBREKgCgrsSQDIppdMFNc8GyRIh2qIkGexcWOBdiO6iU41t 250 anKh+gD3mcn637Hzn0p2AA0TK0D/HzPX9ZCwgGVyoQkXoMa1zWqzQ7QEbk8DmH9M 251 5rr3iw== 252 =4cZl 253 -----END PGP SIGNATURE----- 254 `) 255 signerPubKey = []byte(` 256 -----BEGIN PGP PUBLIC KEY BLOCK----- 257 258 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 305 =8vbI 306 -----END PGP PUBLIC KEY BLOCK----- 307 `) 308 arbitraryPubKey = []byte(` 309 -----BEGIN PGP PUBLIC KEY BLOCK----- 310 311 mQENBF1DABYBCADaEO4PuQjDl91EMSClZeGT6ohkf99BuJpLd+Qfpj5rnJEFwxEr 312 CiykzwQv3vaJR48NrEe2Sa4U6iqGKzI0maDZrWFi8q/4j2hFO4QM8Sa2IWAZKeDa 313 FKR+csrYX0f1PbiTspr+XjdvYKZtaOOs2qkFo5qOscN2rU7rLtK+NBDUR8sx+wDP 314 YI0+B0EpkQ0zSB/se918i2APpleqCXL15E3Ie1u+pBdgLiD5ZN1/iE5Tf+lPCcUT 315 O/stDXzlqz06zVwtSfsX381rz1r+wCsOsTQvpd8d7ztYyMnUwwEOF3b4FukeM+pw 316 TZHfF8yzjSD5rkYMlL4zP4SpROxyJooNApPbABEBAAG0IUFyYml0cmFyeUtleSA8 317 YXJiaXRyYXJ5QHRlc3QuY29tPokBTgQTAQgAOBYhBCHupLgewVEnyziIcws53EZa 318 WzsXBQJdQwAWAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEAs53EZaWzsX 319 4RsIALTjYGwJpf5ZDAIx5v84miI7ArP3J3GFYQnBwIwpEmtKh0Y7RcJFNnHlt5Af 320 Xx4d2HrSxCBD9hiMecLfT73ZQDNLrUeIsv+UJb5JJe/JvQZyD1oWENVHLFzxDC70 321 IJXEd+5goftcsGoXCz+tbk+NAvx1kRG3xbsx6PrKjU4w1d04aBrvcO8rCwYbd1lS 322 yOXWYtRJFK4rGlbReKH50onF65g2Sdzaqx6MPT1gPPjyi3LpuPeTypEdGwrK6eok 323 UqvhLOGiMMXh7s9t6UqfodQ6ayJsimmRw8+tIsSmlMy5cyhcmYQE3+VF2VfTmXv6 324 ELLG0zVBWAeiDYo8AN7hy/MZM6q5AQ0EXUMAFgEIAMLrdMg/FHHFoZV8hv53Bsvd 325 Cr64kx1wxMW72rw3k1Onb4pXmoDNCkkTJqNX+9ocxkgf8eMUJKagKLjF/9c8M6oB 326 SfpL2XfI2WmY3HqhBE8p5WfXY819chH7qhzeDuy36q51CVngDJbl5sS1SIz2xMeC 327 BW+oqSXc59a6KQ+qXQg0iYUCIvfH+3Yi+wlmWQ1QQjKXGmmvJTR6vTx7pwX6awVd 328 HIBUw14A2xwC1uqHD+dC0GMNPTNlT1bP/SJ8F/W8uQxadCFyhjEaFWqnAFSpNwT8 329 mG98DumXbjfhgKPVbSt9uBbMFUXfKpj6uECgvtqVfCpHlQf/tze7gpNsnKgEvdcA 330 EQEAAYkBNgQYAQgAIBYhBCHupLgewVEnyziIcws53EZaWzsXBQJdQwAWAhsMAAoJ 331 EAs53EZaWzsXkW4H/RMaBVx3cR+GQMLJ38MxRuIksV6Fi46AGJJeIp9vqNgYQBdq 332 J3pyPtW0rnBLuqRTZZ+cQOp9mDuaqrblqD9jRm8vKL6vhzRmS1affHD4NPhh1WKH 333 Avi26TEFE1Y/xQ630mcm5K8CF3ItKqO56MSALzpNdc6tDdDflNd7JhkC6iSVKjaE 334 BCR8hH8opNGta0cX0isOVLN1z1bRt/xJTOxjXqoJFcmIuHIOCQzk7ODvqyphaeuV 335 ys/n9RSyDZF01sXnU6LUWsHau5MdFmOZC5oPdWVjB6GIEpZtIccrhm6vH12TVhA3 336 ERUWZPUImhIpQS8TsuwLMkccr7OEXjUayamdBKw= 337 =1rdD 338 -----END PGP PUBLIC KEY BLOCK----- 339 `) 340 )