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

  1. Sanitize plugin_id/namespace to [A-Za-z0-9._-] before calling cache APIs
  2. Run ids through validate_plugin_id (plugin_loader) which enforces the same charset
  3. 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

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


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)