tonhowtf/omniget · error

plugin id must not contain '..

Error message

plugin id must not contain '..': {plugin_id:?}

What it means

Even if each character is legal, an id containing the substring ".." is rejected because it can form traversal sequences when concatenated into paths (e.g. 'a..b' plus separators). validate_plugin_id bails with the offending id embedded.

Solutions

  1. Remove '..' from the id before use
  2. Validate with validate_plugin_id before constructing any path from the id
  3. Reject the manifest/plugin whose id contains '..'

Example fix

// before
let dir = plugin_dir("foo..bar")?; // bails
// after
ensure!(!id.contains(".."), "plugin id must not contain '..'");
let dir = plugin_dir(&id)?;
Defensive patterns

Strategy: validation

Validate before calling

ensure!(!plugin_id.contains(".."), "plugin id must not contain '..': {plugin_id:?}");

Type guard

fn no_dotdot(id: &str) -> bool { !id.contains("..") }

Try / catch

let dir = plugin_dir(&id).with_context(|| format!("traversal-risk plugin id {id:?}"))?;

Prevention

When it happens

Trigger: Passing any id containing ".." (e.g. "foo..bar", "a/../b" already caught earlier) to plugin_dir/validate_plugin_id.

Common situations: User-supplied ids attempting traversal; accidental double-dot typos in manifest ids; concatenated path fragments used as 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/01db34b85858505d. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/plugin_loader.rs:245

///
/// Allowlist e nao blocklist de proposito: o conjunto de ids reais e pequeno e
/// conhecido (`courses`, `study`, `telegram`, `convert`, `misc`), e uma
/// blocklist erra por omissao a cada codificacao nova.
pub fn validate_plugin_id(plugin_id: &str) -> anyhow::Result<()> {
    if plugin_id.is_empty() {
        anyhow::bail!("plugin id must not be empty");
    }
    if plugin_id == "." || plugin_id == ".." || plugin_id.starts_with('.') {
        anyhow::bail!("plugin id must not be a relative path component: {plugin_id:?}");
    }
    if let Some(bad) = plugin_id
        .chars()
        .find(|c| !c.is_ascii_alphanumeric() && !matches!(c, '.' | '_' | '-'))
    {
        anyhow::bail!("plugin id contains an illegal character {bad:?}: {plugin_id:?}");
    }
    if plugin_id.contains("..") {
        anyhow::bail!("plugin id must not contain '..': {plugin_id:?}");
    }
    Ok(())
}

fn load_single_plugin(
    plugin_dir: &Path,
    host: Arc<dyn PluginHost>,
) -> Result<LoadedPlugin, PluginLoadError> {
    let manifest_path = plugin_dir.join("plugin.json");
    let manifest_str = fs::read_to_string(&manifest_path).map_err(|e| {
        PluginLoadError::simple("manifest_read", format!("Cannot read plugin.json: {e}"))
    })?;
    let manifest: PluginManifest = serde_json::from_str(&manifest_str).map_err(|e| {
        PluginLoadError::simple("manifest_parse", format!("Invalid plugin.json: {e}"))
    })?;

    let lib_path =
        find_native_lib(plugin_dir, manifest.rust_crate.as_deref()).ok_or_else(|| {

View on GitHub (pinned to 8600b91f42)