Repository: zellij-org/zellij (language: rust)

Issue:
Load plugins from the web
Right now we are able to load plugins from the local filesystem. We should also be able to load plugins from an http(s) link leading to a compiled wasm file.


Discussion / hints:
Hey! I am checking out zellij and found this tracker through the roadmap. I am interested learning how you plan on plugging up this potential attack vector where a user might load malicious plugins?

With `curl | sh` scripts, its fairly easy to check what the script does. But with a compiled binary, it will be much harder to see what the plugin does.
Good question! This depends on https://github.com/zellij-org/zellij/issues/1395 - to give the user transparency into what the plugin will be allowed to do.

Otherwise, it's about the same answer as running any sort of compiled software on your machine. Don't run software you do not trust.

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 0f02d18b78..f471a020cf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -77,7 +77,7 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
 dependencies = [
- "getrandom 0.2.7",
+ "getrandom 0.2.10",
  "once_cell",
  "version_check",
 ]
@@ -147,16 +147,6 @@ dependencies = [
  "futures-core",
 ]
 
-[[package]]
-name = "async-dup"
-version = "1.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c"
-dependencies = [
- "futures-io",
- "simple-mutex",
-]
-
 [[package]]
 name = "async-executor"
 version = "1.4.1"
@@ -186,23 +176,6 @@ dependencies = [
  "once_cell",
 ]
 
-[[package]]
-name = "async-h1"
-version = "2.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d1d1dae8cb2c4258a79d6ed088b7fb9b4763bf4e9b22d040779761e046a2971"
-dependencies = [
- "async-channel",
- "async-dup",
- "async-global-executor",
- "async-io",
- "futures-lite",
- "http-types",
- "httparse",
- "log",
- "pin-project",
-]
-
 [[package]]
 name = "async-io"
 version = "1.13.0"
@@ -283,19 +256,6 @@ version = "4.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
 
-[[package]]
-name = "async-tls"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d85a97c4a0ecce878efd3f945f119c78a646d8975340bca0398f9bb05c30cc52"
-dependencies = [
- "futures-core",
- "futures-io",
- "rustls",
- "webpki",
- "webpki-roots",
-]
-
 [[package]]
 name = "async-trait"
 version = "0.1.56"
@@ -360,12 +320,6 @@ version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
 
-[[package]]
-name = "base64"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
-
 [[package]]
 name = "base64"
 version = "0.13.0"
@@ -483,6 +437,12 @@ version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
+[[package]]
+name = "bytes"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
+
 [[package]]
 name = "bytes"
 version = "1.4.0"
@@ -503,9 +463,12 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
 
 [[package]]
 name = "cc"
-version = "1.0.73"
+version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "cfg-if"
@@ -651,17 +614,6 @@ dependencies = [
  "crossbeam-utils",
 ]
 
-[[package]]
-name = "config"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3"
-dependencies = [
- "lazy_static",
- "nom 5.1.2",
- "serde",
-]
-
 [[package]]
 name = "console"
 version = "0.15.0"
@@ -907,6 +859,37 @@ dependencies = [
  "cipher",
 ]
 
+[[package]]
+name = "curl"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "winapi",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.68+curl-8.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
+dependencies = [
+ "cc",
+ "libc",
+ "libnghttp2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "daemonize"
 version = "0.4.1"
@@ -951,33 +934,6 @@ dependencies = [
  "syn 1.0.96",
 ]
 
-[[package]]
-name = "dashmap"
-version = "5.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
-dependencies = [
- "cfg-if 1.0.0",
- "hashbrown 0.14.1",
- "lock_api",
- "once_cell",
- "parking_lot_core 0.9.9",
-]
-
-[[package]]
-name = "deadpool"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d126179d86aee4556e54f5f3c6bf6d9884e7cc52cef82f77ee6f90a7747616d"
-dependencies = [
- "async-trait",
- "config",
- "crossbeam-queue",
- "num_cpus",
- "serde",
- "tokio",
-]
-
 [[package]]
 name = "deltae"
 version = "0.3.0"
@@ -1293,6 +1249,17 @@ dependencies = [
  "zellij-tile",
 ]
 
+[[package]]
+name = "flume"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bebadab126f8120d410b677ed95eee4ba6eb7c6dd8e34a5ec88a08050e26132"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "spinning_top",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -1490,9 +1457,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.7"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
@@ -1571,12 +1538,6 @@ dependencies = [
  "ahash",
 ]
 
-[[package]]
-name = "hashbrown"
-version = "0.14.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
-
 [[package]]
 name = "heck"
 version = "0.3.3"
@@ -1639,23 +1600,29 @@ dependencies = [
  "digest 0.9.0",
 ]
 
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes 1.4.0",
+ "fnv",
+ "itoa",
+]
+
 [[package]]
 name = "http-client"
 version = "6.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5"
 dependencies = [
- "async-h1",
  "async-std",
- "async-tls",
  "async-trait",
  "cfg-if 1.0.0",
- "dashmap",
- "deadpool",
- "futures",
  "http-types",
+ "isahc",
  "log",
- "rustls",
 ]
 
 [[package]]
@@ -1680,12 +1647,6 @@ dependencies = [
  "url",
 ]
 
-[[package]]
-name = "httparse"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
-
 [[package]]
 name = "humantime"
 version = "2.1.0"
@@ -1875,6 +1836,29 @@ version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
 
+[[package]]
+name = "isahc"
+version = "0.9.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2948a0ce43e2c2ef11d7edf6816508998d99e13badd1150be0914205df9388a"
+dependencies = [
+ "bytes 0.5.6",
+ "crossbeam-utils",
+ "curl",
+ "curl-sys",
+ "flume",
+ "futures-lite",
+ "http",
+ "log",
+ "once_cell",
+ "slab",
+ "sluice",
+ "tracing",
+ "tracing-futures",
+ "url",
+ "waker-fn",
+]
+
 [[package]]
 name = "itertools"
 version = "0.10.5"
@@ -1964,23 +1948,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "72d9d1bd215936bc8647ad92986bb56f3f216550b53c44ab785e3217ae33625e"
 
 [[package]]
-name = "lexical-core"
-version = "0.7.6"
+name = "libc"
+version = "0.2.149"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
-dependencies = [
- "arrayvec 0.5.2",
- "bitflags",
- "cfg-if 1.0.0",
- "ryu",
- "static_assertions",
-]
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
 
 [[package]]
-name = "libc"
-version = "0.2.140"
+name = "libnghttp2-sys"
+version = "0.1.8+1.55.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
+checksum = "4fae956c192dadcdb5dace96db71fa0b827333cce7c7b38dc71446f024d8a340"
+dependencies = [
+ "cc",
+ "libc",
+]
 
 [[package]]
 name = "libssh2-sys"
@@ -2273,7 +2254,6 @@ version = "5.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
 dependencies = [
- "lexical-core",
  "memchr",
  "version_check",
 ]
@@ -2395,6 +2375,21 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-src"
+version = "300.1.6+3.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "openssl-sys"
 version = "0.9.93"
@@ -2403,6 +2398,7 @@ checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
 dependencies = [
  "cc",
  "libc",
+ "openssl-src",
  "pkg-config",
  "vcpkg",
 ]
@@ -2793,7 +2789,7 @@ version = "0.11.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
 dependencies = [
- "bytes",
+ "bytes 1.4.0",
  "prost-derive",
 ]
 
@@ -2803,7 +2799,7 @@ version = "0.11.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
 dependencies = [
- "bytes",
+ "bytes 1.4.0",
  "heck 0.4.0",
  "itertools",
  "lazy_static",
@@ -2941,7 +2937,7 @@ version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
 dependencies = [
- "getrandom 0.2.7",
+ "getrandom 0.2.10",
 ]
 
 [[package]]
@@ -3010,7 +3006,7 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
 dependencies = [
- "getrandom 0.2.7",
+ "getrandom 0.2.10",
  "redox_syscall 0.2.13",
  "thiserror",
 ]
@@ -3074,21 +3070,6 @@ dependencies = [
  "bytecheck",
 ]
 
-[[package]]
-name = "ring"
-version = "0.16.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
-dependencies = [
- "cc",
- "libc",
- "once_cell",
- "spin",
- "untrusted",
- "web-sys",
- "winapi",
-]
-
 [[package]]
 name = "rkyv"
 version = "0.7.39"
@@ -3175,19 +3156,6 @@ dependencies = [
  "windows-sys 0.45.0",
 ]
 
-[[package]]
-name = "rustls"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81"
-dependencies = [
- "base64 0.12.3",
- "log",
- "ring",
- "sct",
- "webpki",
-]
-
 [[package]]
 name = "ryu"
 version = "1.0.10"
@@ -3204,20 +3172,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "scopeguard"
-version = "1.1.0"
+name = "schannel"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys 0.48.0",
+]
 
 [[package]]
-name = "sct"
-version = "0.6.1"
+name = "scopeguard"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
-dependencies = [
- "ring",
- "untrusted",
-]
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "seahash"
@@ -3452,15 +3419,6 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
 
-[[package]]
-name = "simple-mutex"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6"
-dependencies = [
- "event-listener",
-]
-
 [[package]]
 name = "siphasher"
 version = "0.3.10"
@@ -3498,6 +3456,17 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
 
+[[package]]
+name = "sluice"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5"
+dependencies = [
+ "async-channel",
+ "futures-core",
+ "futures-io",
+]
+
 [[package]]
 name = "smallvec"
 version = "1.8.0"
@@ -3520,12 +3489,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
 [[package]]
 name = "spinning"
 version = "0.1.0"
@@ -3535,6 +3498,15 @@ dependencies = [
  "lock_api",
 ]
 
+[[package]]
+name = "spinning_top"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
+dependencies = [
+ "lock_api",
+]
+
 [[package]]
 name = "ssh2"
 version = "0.9.3"
@@ -3562,12 +3534,6 @@ dependencies = [
  "version_check",
 ]
 
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
 [[package]]
 name = "status-bar"
 version = "0.1.0"
@@ -3736,14 +3702,13 @@ dependencies = [
  "async-trait",
  "cfg-if 1.0.0",
  "futures-util",
- "getrandom 0.2.7",
+ "getrandom 0.2.10",
  "http-client",
  "http-types",
  "log",
  "mime_guess",
  "once_cell",
  "pin-project-lite",
- "rustls",
  "serde",
  "serde_json",
 ]
@@ -4020,17 +3985,6 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8"
 
-[[package]]
-name = "tokio"
-version = "1.29.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
-dependencies = [
- "autocfg",
- "backtrace",
- "pin-project-lite",
-]
-
 [[package]]
 name = "toml"
 version = "0.5.10"
@@ -4073,6 +4027,16 @@ dependencies = [
  "once_cell",
 ]
 
+[[package]]
+name = "tracing-futures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
+dependencies = [
+ "pin-project",
+ "tracing",
+]
+
 [[package]]
 name = "typemap-ors"
 version = "1.0.0"
@@ -4194,12 +4158,6 @@ dependencies = [
  "destructure_traitobject",
 ]
 
-[[package]]
-name = "untrusted"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
-
 [[package]]
 name = "url"
 version = "2.2.2"
@@ -4225,7 +4183,7 @@ version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
 dependencies = [
- "getrandom 0.2.7",
+ "getrandom 0.2.10",
  "serde",
 ]
 
@@ -4429,7 +4387,7 @@ version = "3.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "840af6d21701220cb805dc7201af301cb99e9b4f646f48a41befbc1d949f0f90"
 dependencies = [
- "bytes",
+ "bytes 1.4.0",
  "cfg-if 1.0.0",
  "indexmap",
  "js-sys",
@@ -4590,7 +4548,7 @@ version = "3.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9bc4fe3b48ccc620901bdcdfac98d8a76ef3487412c221752814750c2e7db4c1"
 dependencies = [
- "bytes",
+ "bytes 1.4.0",
  "thiserror",
  "wasmer-vfs",
 ]
@@ -4601,11 +4559,11 @@ version = "3.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e893ecd57c63db83b17dacfaee90f660e1d7f5b26d2f9d88ea6aa2e8c4bc301d"
 dependencies = [
- "bytes",
+ "bytes 1.4.0",
  "cfg-if 1.0.0",
  "derivative",
  "generational-arena",
- "getrandom 0.2.7",
+ "getrandom 0.2.10",
  "libc",
  "thiserror",
  "tracing",
@@ -4625,7 +4583,7 @@ version = "3.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd65882d8cee793848776f26e979b7ff3e139d927947d87304c940c89527a730"
 dependencies = [
- "bytes",
+ "bytes 1.4.0",
  "tracing",
  "wasmer-vfs",
  "wasmer-vnet",
@@ -4752,25 +4710,6 @@ dependencies = [
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "webpki"
-version = "0.21.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f"
-dependencies = [
- "webpki",
-]
-
 [[package]]
 name = "wepoll-ffi"
 version = "0.1.2"
@@ -5193,6 +5132,7 @@ dependencies = [
  "crossbeam",
  "directories",
  "expect-test",
+ "futures",
  "humantime",
  "include_dir",
  "insta",
@@ -5206,6 +5146,7 @@ dependencies = [
  "nix 0.23.1",
  "notify-debouncer-full",
  "once_cell",
+ "openssl-sys",
  "percent-encoding",
  "prost",
  "prost-build",
diff --git a/src/main.rs b/src/main.rs
index 3d1f37f383..72dce0d26c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -46,6 +46,28 @@ fn main() {
             commands::send_action_to_session(command_cli_action, opts.session, config);
             std::process::exit(0);
         }
+        if let Some(Command::Sessions(Sessions::Plugin {
+            url,
+            floating,
+            in_place,
+            configuration,
+        })) = opts.command
+        {
+            let command_cli_action = CliAction::NewPane {
+                command: vec![],
+                plugin: Some(url),
+                direction: None,
+                cwd: None,
+                floating,
+                in_place,
+                name: None,
+                close_on_exit: false,
+                start_suspended: false,
+                configuration,
+            };
+            commands::send_action_to_session(command_cli_action, opts.session, config);
+            std::process::exit(0);
+        }
         if let Some(Command::Sessions(Sessions::Edit {
             file,
             direction,
diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs
index b2ddb99249..260740f9a5 100644
--- a/zellij-server/src/plugins/wasm_bridge.rs
+++ b/zellij-server/src/plugins/wasm_bridge.rs
@@ -4,6 +4,7 @@ use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugi
 use crate::plugins::plugin_worker::MessageToWorker;
 use crate::plugins::watch_filesystem::watch_filesystem;
 use crate::plugins::zellij_exports::{wasi_read_string, wasi_write_object};
+use highway::{HighwayHash, PortableHash};
 use log::info;
 use std::{
     collections::{HashMap, HashSet},
@@ -14,7 +15,10 @@ use std::{
 use wasmer::{Module, Store, Value};
 use zellij_utils::async_channel::Sender;
 use zellij_utils::async_std::task::{self, JoinHandle};
+use zellij_utils::consts::ZELLIJ_CACHE_DIR;
 use zellij_utils::data::{PermissionStatus, PermissionType};
+use zellij_utils::downloader::download::Download;
+use zellij_utils::downloader::Downloader;
 use zellij_utils::input::permission::PermissionCache;
 use zellij_utils::notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap};
 use zellij_utils::plugin_api::event::ProtobufEvent;
@@ -129,7 +133,7 @@ impl WasmBridge {
 
         let plugin_id = self.next_plugin_id;
 
-        let plugin = self
+        let mut plugin = self
             .plugins
             .get(run)
             .with_context(|| format!("failed to resolve plugin {run:?}"))
@@ -158,6 +162,33 @@ impl WasmBridge {
                 let _ =
                     senders.send_to_background_jobs(BackgroundJob::AnimatePluginLoading(plugin_id));
                 let mut loading_indication = LoadingIndication::new(plugin_name.clone());
+
+                if let RunPluginLocation::Remote(url) = &plugin.location {
+                    let download = Download::from(url);
+
+                    let hash: String = PortableHash::default()
+                        .hash128(download.url.as_bytes())
+                        .iter()
+                        .map(ToString::to_string)
+                        .collect();
+
+                    let plugin_directory = ZELLIJ_CACHE_DIR.join(hash);
+
+                    // The plugin path is determined by the hash of the plugin URL in the cache directory.
+                    plugin.path = plugin_directory.join(&download.file_name);
+
+                    let downloader = Downloader::new(plugin_directory);
+                    match downloader.fetch(&download).await {
+                        Ok(_) => {},
+                        Err(e) => handle_plugin_loading_failure(
+                            &senders,
+                            plugin_id,
+                            &mut loading_indication,
+                            e,
+                        ),
+                    }
+                }
+
                 match PluginLoader::start_plugin(
                     plugin_id,
                     client_id,
diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml
index 8845b64f8f..bb7d030873 100644
--- a/zellij-utils/Cargo.toml
+++ b/zellij-utils/Cargo.toml
@@ -54,7 +54,11 @@ interprocess = "1.2.1"
 async-std = { version = "1.3.0", features = ["unstable"] }
 notify-debouncer-full = "0.1.0"
 humantime = "2.1.0"
-surf = { version = "2.3.2", default-features = false, features = ["h1-client-rustls"] }
+futures = "0.3.28"
+surf = { version = "2.3.2", default-features = false, features = [
+    "curl-client",
+] }
+openssl-sys = { version = "0.9.93", features = ["vendored"] }
 
 [dev-dependencies]
 insta = { version = "1.6.0", features = ["backtrace"] }
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index 79aad99473..c196c17d18 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -217,6 +217,32 @@ pub enum Sessions {
         #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
         start_suspended: bool,
     },
+    /// Load a plugin
+    #[clap(visible_alias = "r")]
+    Plugin {
+        /// Plugin URL, can either start with http(s), file: or zellij:
+        #[clap(last(true), required(true))]
+        url: String,
+
+        /// Plugin configuration
+        #[clap(short, long, value_parser)]
+        configuration: Option<PluginUserConfiguration>,
+
+        /// Open the new pane in floating mode
+        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
+        floating: bool,
+
+        /// Open the new pane in place of the current pane, temporarily suspending it
+        #[clap(
+            short,
+            long,
+            value_parser,
+            default_value("false"),
+            takes_value(false),
+            conflicts_with("floating")
+        )]
+        in_place: bool,
+    },
     /// Edit file with default $EDITOR / $VISUAL
     #[clap(visible_alias = "e")]
     Edit {
diff --git a/zellij-utils/src/downloader/download.rs b/zellij-utils/src/downloader/download.rs
new file mode 100644
index 0000000000..d665f7e54c
--- /dev/null
+++ b/zellij-utils/src/downloader/download.rs
@@ -0,0 +1,49 @@
+use serde::{Deserialize, Serialize};
+use surf::Url;
+
+#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
+pub struct Download {
+    pub url: String,
+    pub file_name: String,
+}
+
+impl Download {
+    pub fn from(url: &str) -> Self {
+        match Url::parse(url) {
+            Ok(u) => u
+                .path_segments()
+                .map_or_else(Download::default, |segments| {
+                    let file_name = segments.last().unwrap_or("").to_string();
+
+                    Download {
+                        url: url.to_string(),
+                        file_name,
+                    }
+                }),
+            Err(_) => Download::default(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_from_download() {
+        let download = Download::from("https://github.com/example/plugin.wasm");
+        assert_eq!(download.url, "https://github.com/example/plugin.wasm");
+        assert_eq!(download.file_name, "plugin.wasm");
+    }
+
+    #[test]
+    fn test_empty_download() {
+        let d1 = Download::from("https://example.com");
+        assert_eq!(d1.url, "https://example.com");
+        assert_eq!(d1.file_name, "");
+
+        let d2 = Download::from("github.com");
+        assert_eq!(d2.url, "");
+        assert_eq!(d2.file_name, "");
+    }
+}
diff --git a/zellij-utils/src/downloader/mod.rs b/zellij-utils/src/downloader/mod.rs
new file mode 100644
index 0000000000..b0b2771ddb
--- /dev/null
+++ b/zellij-utils/src/downloader/mod.rs
@@ -0,0 +1,147 @@
+pub mod download;
+
+use async_std::{
+    fs::{create_dir_all, File},
+    io::{ReadExt, WriteExt},
+    stream, task,
+};
+use futures::{StreamExt, TryStreamExt};
+use std::path::PathBuf;
+use surf::Client;
+use thiserror::Error;
+
+use self::download::Download;
+
+#[derive(Error, Debug)]
+pub enum DownloaderError {
+    #[error("RequestError: {0}")]
+    Request(surf::Error),
+    #[error("StatusError: {0}, StatusCode: {1}")]
+    Status(String, surf::StatusCode),
+    #[error("IoError: {0}")]
+    Io(#[source] std::io::Error),
+    #[error("IoPathError: {0}, File: {1}")]
+    IoPath(std::io::Error, PathBuf),
+}
+
+#[derive(Default, Debug)]
+pub struct Downloader {
+    client: Client,
+    directory: PathBuf,
+}
+
+impl Downloader {
+    pub fn new(directory: PathBuf) -> Self {
+        Self {
+            client: surf::client().with(surf::middleware::Redirect::default()),
+            directory,
+        }
+    }
+
+    pub fn set_directory(&mut self, directory: PathBuf) {
+        self.directory = directory;
+    }
+
+    pub fn download(&self, downloads: &[Download]) -> Vec<Result<(), DownloaderError>> {
+        task::block_on(async {
+            stream::from_iter(downloads)
+                .map(|download| self.fetch(download))
+                .buffer_unordered(4)
+                .collect::<Vec<_>>()
+                .await
+        })
+    }
+
+    pub async fn fetch(&self, download: &Download) -> Result<(), DownloaderError> {
+        let mut file_size: usize = 0;
+
+        let file_path = self.directory.join(&download.file_name);
+
+        if file_path.exists() {
+            file_size = match file_path.metadata() {
+                Ok(metadata) => metadata.len() as usize,
+                Err(e) => return Err(DownloaderError::IoPath(e, file_path)),
+            }
+        }
+
+        let response = self
+            .client
+            .get(&download.url)
+            .await
+            .map_err(|e| DownloaderError::Request(e))?;
+        let status = response.status();
+
+        if status.is_client_error() || status.is_server_error() {
+            return Err(DownloaderError::Status(
+                status.canonical_reason().to_string(),
+                status,
+            ));
+        }
+
+        let length = response.len().unwrap_or(0);
+        if length > 0 && length == file_size {
+            return Ok(());
+        }
+
+        let mut dest = {
+            create_dir_all(&self.directory)
+                .await
+                .map_err(|e| DownloaderError::IoPath(e, self.directory.clone()))?;
+            File::create(&file_path)
+                .await
+                .map_err(|e| DownloaderError::IoPath(e, file_path))?
+        };
+
+        let mut bytes = response.bytes();
+        while let Some(byte) = bytes.try_next().await.map_err(DownloaderError::Io)? {
+            dest.write_all(&[byte]).await.map_err(DownloaderError::Io)?;
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use tempfile::tempdir;
+
+    #[test]
+    #[ignore]
+    fn test_fetch_plugin() {
+        let dir = tempdir().expect("could not get temp dir");
+        let dir_path = dir.path();
+
+        let downloader = Downloader::new(dir_path.to_path_buf());
+        let dl = Download::from(
+            "https://github.com/imsnif/monocle/releases/download/0.37.2/monocle.wasm",
+        );
+
+        let result = task::block_on(downloader.fetch(&dl));
+
+        assert!(result.is_ok());
+    }
+
+    #[test]
+    #[ignore]
+    fn test_download_plugins() {
+        let dir = tempdir().expect("could not get temp dir");
+        let dir_path = dir.path();
+
+        let downloader = Downloader::new(dir_path.to_path_buf());
+        let downloads = vec![
+            Download::from(
+                "https://github.com/imsnif/monocle/releases/download/0.37.2/monocle.wasm",
+            ),
+            Download::from(
+                "https://github.com/imsnif/multitask/releases/download/0.38.2/multitask.wasm",
+            ),
+        ];
+
+        let results = downloader.download(&downloads);
+        for result in results {
+            assert!(result.is_ok())
+        }
+    }
+}
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index ace471f8f0..312286ec0b 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -287,6 +287,7 @@ impl FromStr for PluginUserConfiguration {
 pub enum RunPluginLocation {
     File(PathBuf),
     Zellij(PluginTag),
+    Remote(String),
 }
 
 impl Default for RunPluginLocation {
@@ -332,6 +333,7 @@ impl RunPluginLocation {
                 };
                 Ok(Self::File(path))
             },
+            "https" | "http" => Ok(Self::Remote(url.as_str().to_owned())),
             _ => Err(PluginsConfigError::InvalidUrlScheme(url)),
         }
     }
@@ -339,6 +341,7 @@ impl RunPluginLocation {
         match self {
             RunPluginLocation::File(pathbuf) => format!("file:{}", pathbuf.display()),
             RunPluginLocation::Zellij(plugin_tag) => format!("zellij:{}", plugin_tag),
+            RunPluginLocation::Remote(url) => format!("remote:{}", url),
         }
     }
 }
@@ -351,6 +354,7 @@ impl From<&RunPluginLocation> for Url {
                 path.clone().into_os_string().into_string().unwrap()
             ),
             RunPluginLocation::Zellij(tag) => format!("zellij:{}", tag),
+            RunPluginLocation::Remote(url) => format!("remote:{}", url),
         };
         Self::parse(&url).unwrap()
     }
@@ -364,8 +368,8 @@ impl fmt::Display for RunPluginLocation {
                 "{}",
                 path.clone().into_os_string().into_string().unwrap()
             ),
-
             Self::Zellij(tag) => write!(f, "{}", tag),
+            Self::Remote(url) => write!(f, "{}", url),
         }
     }
 }
diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs
index f12aba2a20..2a262222fa 100644
--- a/zellij-utils/src/input/plugins.rs
+++ b/zellij-utils/src/input/plugins.rs
@@ -55,6 +55,13 @@ impl PluginsConfig {
                 userspace_configuration: run.configuration.clone(),
                 ..plugin
             }),
+            RunPluginLocation::Remote(_) => Some(PluginConfig {
+                path: PathBuf::new(),
+                run: PluginType::Pane(None),
+                _allow_exec_host_cmd: run._allow_exec_host_cmd,
+                location: run.location.clone(),
+                userspace_configuration: run.configuration.clone(),
+            }),
         }
     }
 
@@ -231,7 +238,7 @@ pub enum PluginsConfigError {
     DuplicatePlugins(PluginTag),
     #[error("Failed to parse url: {0:?}")]
     InvalidUrl(#[from] url::ParseError),
-    #[error("Only 'file:' and 'zellij:' url schemes are supported for plugin lookup. '{0}' does not match either.")]
+    #[error("Only 'file:', 'http(s):' and 'zellij:' url schemes are supported for plugin lookup. '{0}' does not match either.")]
     InvalidUrlScheme(Url),
     #[error("Could not find plugin at the path: '{0:?}'")]
     InvalidPluginLocation(PathBuf),
diff --git a/zellij-utils/src/lib.rs b/zellij-utils/src/lib.rs
index 5438f32389..05752e21ab 100644
--- a/zellij-utils/src/lib.rs
+++ b/zellij-utils/src/lib.rs
@@ -17,6 +17,8 @@ pub mod shared;
 #[cfg(not(target_family = "wasm"))]
 pub mod channels; // Requires async_std
 #[cfg(not(target_family = "wasm"))]
+pub mod downloader; // Requires async_std
+#[cfg(not(target_family = "wasm"))]
 pub mod ipc; // Requires interprocess
 #[cfg(not(target_family = "wasm"))]
 pub mod logging; // Requires log4rs


Test patch: