tonhowtf/omniget · error
external_data_cache: plugin_id/namespace must not contain…
Error message
external_data_cache: plugin_id/namespace must not contain path separators or null bytes
What it means
`external_data_cache` rejects plugin_id or namespace strings containing '/', '\\', ':' or NUL, because these values are interpolated into filesystem paths and could traverse out of the cache directory or create invalid paths. This is a path-traversal guard.
Solutions
- Sanitize plugin_id/namespace to [A-Za-z0-9._-] before calling cache APIs
- Run ids through validate_plugin_id (plugin_loader) which enforces the same charset
- Treat any plugin supplying such ids as invalid and refuse to load it
Example fix
// before host.external_data_cache(&raw_id, &ns)?; // after let safe_id: String = raw_id.chars().filter(|c| c.is_ascii_alphanumeric() || matches!(c, '.'|'_'|'-')).collect(); ensure!(safe_id == raw_id, "illegal chars in plugin id"); host.external_data_cache(&safe_id, &ns)?;
Defensive patterns
Strategy: validation
Validate before calling
fn safe_component(s: &str) -> bool { !s.is_empty() && !s.contains(['/', '\\', ':', '\0']) }
ensure!(safe_component(plugin_id) && safe_component(namespace), "unsafe cache path component"); Type guard
fn is_path_safe(s: &str) -> bool { !s.is_empty() && !s.contains(['/', '\\', ':', '\0']) } Try / catch
let cache = host.external_data_cache(&id, &ns)
.with_context(|| format!("unsafe cache id {id:?}/{ns:?}"))?; Prevention
- Whitelist charset [A-Za-z0-9._-] for all plugin identifiers
- Never trust manifest fields in path construction
- Run ids through validate_plugin_id before any filesystem use
When it happens
Trigger: A plugin id or namespace containing a path separator, drive colon, or null byte is passed to external_data_cache — usually from untrusted manifest fields or externally supplied plugin identifiers.
Common situations: Malicious or buggy plugin manifest with id like '../evil' or 'a/b'; namespace built from user input including ':'; Windows drive letters embedded in ids.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- plugin id must not be a relative path component
- plugin id must not contain '..
- external_data_cache: plugin_id must not be empty
- external_data_cache: namespace must not be empty
- plugin id must not be empty
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/0250d54915a82c31.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/plugin_host.rs:122
which::which(&bin_name).ok()
}
fn default_output_dir(&self) -> PathBuf {
dirs::download_dir()
.or_else(dirs::home_dir)
.unwrap_or_else(|| PathBuf::from("."))
}
fn external_data_cache(&self, plugin_id: &str, namespace: &str) -> anyhow::Result<PathBuf> {
if plugin_id.is_empty() {
anyhow::bail!("external_data_cache: plugin_id must not be empty");
}
if namespace.is_empty() {
anyhow::bail!("external_data_cache: namespace must not be empty");
}
if plugin_id.contains(['/', '\\', ':', '\0']) || namespace.contains(['/', '\\', ':', '\0'])
{
anyhow::bail!(
"external_data_cache: plugin_id/namespace must not contain path separators or null bytes"
);
}
// portable installs keep every file next to the app, so the cache
// lives under the app data dir instead of the OS cache dir
let base = if std::env::var("OMNIGET_PORTABLE").is_ok() {
omniget_core::core::paths::app_data_dir()
.ok_or_else(|| anyhow::anyhow!("external_data_cache: app data dir unavailable"))?
.join("cache")
} else {
dirs::cache_dir()
.ok_or_else(|| {
anyhow::anyhow!(
"external_data_cache: OS cache dir unavailable on this platform"
)
})?
.join("wtf.tonho.omniget")View on GitHub (pinned to 8600b91f42)