use crate::plugins::host_dir::{HostDirFilesystem, HostDirReadLimitContext};
use secure_exec_kernel::mount_plugin::{
FileSystemPluginFactory, OpenFileSystemPluginRequest, PluginError,
};
use secure_exec_kernel::mount_table::{
MountedFileSystem, MountedVirtualFileSystem, ReadOnlyFileSystem,
};
use serde::Deserialize;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ModuleAccessMountConfig {
host_path: String,
}
#[derive(Debug)]
pub(crate) struct ModuleAccessMountPlugin;
impl<Context> FileSystemPluginFactory<Context> for ModuleAccessMountPlugin
where
Context: HostDirReadLimitContext,
{
fn plugin_id(&self) -> &'static str {
"module_access"
}
fn open(
&self,
request: OpenFileSystemPluginRequest<'_, Context>,
) -> Result<Box<dyn MountedFileSystem>, PluginError> {
let config: ModuleAccessMountConfig = serde_json::from_value(request.config.clone())
.map_err(|error| PluginError::invalid_input(error.to_string()))?;
let host_path = validate_module_access_root(&config.host_path)?;
let filesystem = HostDirFilesystem::new_with_read_limit(
&host_path,
request.context.host_dir_max_read_bytes(),
)
.map_err(|error| PluginError::invalid_input(error.to_string()))?;
Ok(Box::new(ReadOnlyFileSystem::new(
MountedVirtualFileSystem::new(filesystem),
)))
}
}
fn validate_module_access_root(path: &str) -> Result<PathBuf, PluginError> {
let root = fs::canonicalize(path).map_err(|error| {
PluginError::invalid_input(format!(
"failed to resolve module_access root {path}: {error}"
))
})?;
if root.file_name() == Some(Path::new("node_modules").as_os_str()) {
return Ok(root);
}
Err(PluginError::invalid_input(format!(
"module_access roots must resolve to a node_modules directory: {path}"
)))
}