mirror of
https://github.com/extism/extism.git
synced 2026-01-12 07:18:02 -05:00
Compare commits
5 Commits
fix-clippy
...
allowed-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fad7eb4454 | ||
|
|
37e0e2fed4 | ||
|
|
5b94feb7ec | ||
|
|
6146d2f47c | ||
|
|
424e6c328a |
@@ -13,7 +13,7 @@ description = "Traits to make Rust types usable with Extism"
|
||||
anyhow = "1.0.75"
|
||||
base64 = "~0.22"
|
||||
bytemuck = {version = "1.14.0", optional = true }
|
||||
prost = { version = "0.14.1", optional = true }
|
||||
prost = { version = "0.13.1", optional = true }
|
||||
protobuf = { version = "3.2.0", optional = true }
|
||||
rmp-serde = { version = "1.1.2", optional = true }
|
||||
serde = "1.0.186"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
mod local_path;
|
||||
|
||||
pub use local_path::LocalPath;
|
||||
|
||||
#[deprecated]
|
||||
pub type ManifestMemory = MemoryOptions;
|
||||
|
||||
@@ -279,7 +283,7 @@ pub struct Manifest {
|
||||
/// the path on disk to the path it should be available inside the plugin.
|
||||
/// For example, `".": "/tmp"` would mount the current directory as `/tmp` inside the module
|
||||
#[serde(default)]
|
||||
pub allowed_paths: Option<BTreeMap<String, PathBuf>>,
|
||||
pub allowed_paths: Option<BTreeMap<PathBuf, LocalPath>>,
|
||||
|
||||
/// The plugin timeout in milliseconds
|
||||
#[serde(default)]
|
||||
@@ -337,15 +341,15 @@ impl Manifest {
|
||||
}
|
||||
|
||||
/// Add a path to `allowed_paths`
|
||||
pub fn with_allowed_path(mut self, src: String, dest: impl AsRef<Path>) -> Self {
|
||||
pub fn with_allowed_path(mut self, src: impl Into<LocalPath>, dest: impl AsRef<Path>) -> Self {
|
||||
let dest = dest.as_ref().to_path_buf();
|
||||
match &mut self.allowed_paths {
|
||||
Some(p) => {
|
||||
p.insert(src, dest);
|
||||
p.insert(dest, src.into());
|
||||
}
|
||||
None => {
|
||||
let mut p = BTreeMap::new();
|
||||
p.insert(src, dest);
|
||||
p.insert(dest, src.into());
|
||||
self.allowed_paths = Some(p);
|
||||
}
|
||||
}
|
||||
@@ -354,8 +358,8 @@ impl Manifest {
|
||||
}
|
||||
|
||||
/// Set `allowed_paths`
|
||||
pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (String, PathBuf)>) -> Self {
|
||||
self.allowed_paths = Some(paths.collect());
|
||||
pub fn with_allowed_paths(mut self, paths: impl Iterator<Item = (LocalPath, PathBuf)>) -> Self {
|
||||
self.allowed_paths = Some(paths.map(|(local, wasm)| (wasm, local)).collect());
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
119
manifest/src/local_path.rs
Normal file
119
manifest/src/local_path.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
|
||||
pub enum LocalPath {
|
||||
ReadOnly(PathBuf),
|
||||
ReadWrite(PathBuf),
|
||||
}
|
||||
|
||||
impl LocalPath {
|
||||
pub fn as_path(&self) -> &Path {
|
||||
match self {
|
||||
LocalPath::ReadOnly(p) => p.as_path(),
|
||||
LocalPath::ReadWrite(p) => p.as_path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for LocalPath {
|
||||
fn from(value: &str) -> Self {
|
||||
if let Some(s) = value.strip_prefix("ro:") {
|
||||
LocalPath::ReadOnly(PathBuf::from(s))
|
||||
} else {
|
||||
LocalPath::ReadWrite(PathBuf::from(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for LocalPath {
|
||||
fn from(value: String) -> Self {
|
||||
LocalPath::from(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathBuf> for LocalPath {
|
||||
fn from(value: PathBuf) -> Self {
|
||||
LocalPath::ReadWrite(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Path> for LocalPath {
|
||||
fn from(value: &Path) -> Self {
|
||||
LocalPath::ReadWrite(value.to_path_buf())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for LocalPath {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
LocalPath::ReadOnly(path) => {
|
||||
let s = match path.to_str() {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(serde::ser::Error::custom(
|
||||
"Path contains invalid UTF-8 characters",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
format!("ro:{s}").serialize(serializer)
|
||||
}
|
||||
LocalPath::ReadWrite(path) => path.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalPathVisitor;
|
||||
|
||||
impl serde::de::Visitor<'_> for LocalPathVisitor {
|
||||
type Value = LocalPath;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("path string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(From::from(v))
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(From::from(v))
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
std::str::from_utf8(v)
|
||||
.map(From::from)
|
||||
.map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Bytes(v), &self))
|
||||
}
|
||||
|
||||
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
String::from_utf8(v).map(From::from).map_err(|e| {
|
||||
serde::de::Error::invalid_value(serde::de::Unexpected::Bytes(&e.into_bytes()), &self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for LocalPath {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_string(LocalPathVisitor)
|
||||
}
|
||||
}
|
||||
@@ -112,8 +112,7 @@ let mut plugin = Plugin::new(&manifest, [], true);
|
||||
let res = plugin.call::<&str, &str>("count_vowels", "Yellow, world!").unwrap();
|
||||
println!("{}", res);
|
||||
# => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
|
||||
let manifest = Manifest::new([url]).with_config_key("vowels", "aeiouyAEIOUY");
|
||||
let mut plugin = Plugin::new(&manifest, [], true).unwrap();
|
||||
let mut plugin = Plugin::new(&manifest, [], true).with_config_key("vowels", "aeiouyAEIOUY");
|
||||
let res = plugin.call::<&str, &str>("count_vowels", "Yellow, world!").unwrap();
|
||||
println!("{}", res);
|
||||
# => {"count": 4, "total": 4, "vowels": "aeiouyAEIOUY"}
|
||||
|
||||
@@ -16,7 +16,7 @@ fn main() {
|
||||
|
||||
let res = plugin.call::<&str, &str>("try_read", "").unwrap();
|
||||
|
||||
println!("{res:?}");
|
||||
println!("{:?}", res);
|
||||
|
||||
println!("-----------------------------------------------------");
|
||||
|
||||
@@ -30,7 +30,7 @@ fn main() {
|
||||
);
|
||||
let res2 = plugin.call::<&str, &str>("try_write", &line).unwrap();
|
||||
|
||||
println!("{res2:?}");
|
||||
println!("{:?}", res2);
|
||||
|
||||
println!("done!");
|
||||
}
|
||||
|
||||
@@ -30,6 +30,6 @@ fn main() {
|
||||
let res = plugin
|
||||
.call::<&str, &str>("reflect", "Hello, world!")
|
||||
.unwrap();
|
||||
println!("{res}");
|
||||
println!("{}", res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,6 @@ fn main() {
|
||||
println!("Dumping logs");
|
||||
|
||||
for line in LOGS.lock().unwrap().iter() {
|
||||
print!("{line}");
|
||||
print!("{}", line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,6 @@ fn main() {
|
||||
let res = plugin
|
||||
.call::<&str, &str>("count_vowels", "Hello, world!")
|
||||
.unwrap();
|
||||
println!("{res}");
|
||||
println!("{}", res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,9 +352,9 @@ impl CurrentPlugin {
|
||||
|
||||
if let Some(a) = &manifest.allowed_paths {
|
||||
for (k, v) in a.iter() {
|
||||
let readonly = k.starts_with("ro:");
|
||||
let readonly = matches!(v, extism_manifest::LocalPath::ReadOnly(_));
|
||||
|
||||
let dir_path = if readonly { &k[3..] } else { k };
|
||||
let dir_path = v.as_path();
|
||||
|
||||
let dir = wasi_common::sync::dir::Dir::from_cap_std(
|
||||
wasi_common::sync::Dir::open_ambient_dir(dir_path, auth)?,
|
||||
@@ -366,7 +366,7 @@ impl CurrentPlugin {
|
||||
Box::new(dir)
|
||||
};
|
||||
|
||||
ctx.push_preopened_dir(file, v)?;
|
||||
ctx.push_preopened_dir(file, k)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ pub fn set_log_callback<F: 'static + Clone + Fn(&str)>(
|
||||
let x = tracing_subscriber::EnvFilter::builder()
|
||||
.with_default_directive(tracing::Level::ERROR.into());
|
||||
if is_level {
|
||||
x.parse_lossy(format!("extism={filter}"))
|
||||
x.parse_lossy(format!("extism={}", filter))
|
||||
} else {
|
||||
x.parse_lossy(filter)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::*;
|
||||
fn hex(data: &[u8]) -> String {
|
||||
let mut s = String::new();
|
||||
for &byte in data {
|
||||
write!(&mut s, "{byte:02x}").unwrap();
|
||||
write!(&mut s, "{:02x}", byte).unwrap();
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
@@ -963,7 +963,8 @@ impl Plugin {
|
||||
}
|
||||
Err(msg) => {
|
||||
res = Err(Error::msg(format!(
|
||||
"unable to load error message from memory: {msg}",
|
||||
"unable to load error message from memory: {}",
|
||||
msg,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,7 +871,7 @@ fn set_log_file(log_file: impl Into<std::path::PathBuf>, filter: &str) -> Result
|
||||
let x = tracing_subscriber::EnvFilter::builder()
|
||||
.with_default_directive(tracing::Level::ERROR.into());
|
||||
if is_level {
|
||||
x.parse_lossy(format!("extism={filter}"))
|
||||
x.parse_lossy(format!("extism={}", filter))
|
||||
} else {
|
||||
x.parse_lossy(filter)
|
||||
}
|
||||
@@ -926,7 +926,7 @@ unsafe fn set_log_buffer(filter: &str) -> Result<(), Error> {
|
||||
let x = tracing_subscriber::EnvFilter::builder()
|
||||
.with_default_directive(tracing::Level::ERROR.into());
|
||||
if is_level {
|
||||
x.parse_lossy(format!("extism={filter}"))
|
||||
x.parse_lossy(format!("extism={}", filter))
|
||||
} else {
|
||||
x.parse_lossy(filter)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fn test_issue_620() {
|
||||
// Call test method, this does not work
|
||||
let p = plugin.call::<(), String>("test", ()).unwrap();
|
||||
|
||||
println!("{p}");
|
||||
println!("{}", p);
|
||||
}
|
||||
|
||||
// https://github.com/extism/extism/issues/619
|
||||
@@ -53,5 +53,5 @@ fn test_issue_775() {
|
||||
Ok(code) => Err(code),
|
||||
}
|
||||
.unwrap();
|
||||
println!("{p}");
|
||||
println!("{}", p);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ fn run_thread(p: Pool, i: u64) -> std::thread::JoinHandle<()> {
|
||||
.unwrap()
|
||||
.call("count_vowels", "abc")
|
||||
.unwrap();
|
||||
println!("{s}");
|
||||
println!("{}", s);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,8 @@ fn it_works() {
|
||||
let native_avg: std::time::Duration = native_sum / native_num_tests as u32;
|
||||
|
||||
println!(
|
||||
"native function call (avg, N = {native_num_tests}): {native_avg:?}"
|
||||
"native function call (avg, N = {}): {:?}",
|
||||
native_num_tests, native_avg
|
||||
);
|
||||
|
||||
let num_tests = test_times.len();
|
||||
@@ -144,7 +145,7 @@ fn it_works() {
|
||||
.unwrap();
|
||||
let avg: std::time::Duration = sum / num_tests as u32;
|
||||
|
||||
println!("wasm function call (avg, N = {num_tests}): {avg:?}");
|
||||
println!("wasm function call (avg, N = {}): {:?}", num_tests, avg);
|
||||
|
||||
// Check that log file was written to
|
||||
if log {
|
||||
@@ -211,7 +212,7 @@ fn test_cancel() {
|
||||
let _output: Result<&[u8], Error> = plugin.call("loop_forever", "abc123");
|
||||
let end = std::time::Instant::now();
|
||||
let time = end - start;
|
||||
println!("Cancelled plugin ran for {time:?}");
|
||||
println!("Cancelled plugin ran for {:?}", time);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +271,7 @@ fn test_fuel_consumption() {
|
||||
assert!(output.is_err());
|
||||
|
||||
let fuel_consumed = plugin.fuel_consumed().unwrap();
|
||||
println!("Fuel consumed: {fuel_consumed}");
|
||||
println!("Fuel consumed: {}", fuel_consumed);
|
||||
assert!(fuel_consumed > 0);
|
||||
}
|
||||
|
||||
@@ -439,7 +440,7 @@ fn test_memory_max() {
|
||||
assert!(output.is_err());
|
||||
|
||||
let err = output.unwrap_err().root_cause().to_string();
|
||||
println!("{err:?}");
|
||||
println!("{:?}", err);
|
||||
assert_eq!(err, "oom");
|
||||
|
||||
// Should pass with memory.max set to a large enough number
|
||||
@@ -502,7 +503,7 @@ fn test_extism_error() {
|
||||
let mut plugin = Plugin::new(&manifest, [f], true).unwrap();
|
||||
let output: Result<String, Error> = plugin.call("count_vowels", "a".repeat(1024));
|
||||
assert!(output.is_err());
|
||||
println!("{output:?}");
|
||||
println!("{:?}", output);
|
||||
assert_eq!(output.unwrap_err().root_cause().to_string(), "TEST");
|
||||
}
|
||||
|
||||
@@ -822,7 +823,7 @@ fn test_http_response_headers() {
|
||||
.unwrap();
|
||||
let req = HttpRequest::new("https://extism.org");
|
||||
let Json(res): Json<HashMap<String, String>> = plugin.call("http_get", Json(req)).unwrap();
|
||||
println!("{res:?}");
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res["content-type"], "text/html; charset=utf-8");
|
||||
}
|
||||
|
||||
@@ -837,6 +838,6 @@ fn test_http_response_headers_disabled() {
|
||||
.unwrap();
|
||||
let req = HttpRequest::new("https://extism.org");
|
||||
let Json(res): Json<HashMap<String, String>> = plugin.call("http_get", Json(req)).unwrap();
|
||||
println!("{res:?}");
|
||||
println!("{:?}", res);
|
||||
assert!(res.is_empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user