tonhowtf/omniget · error
plugin id must not be a relative path component
Error message
plugin id must not be a relative path component: {plugin_id:?} What it means
`validate_plugin_id` rejects ids equal to "." or ".." or starting with '.', because such ids are relative path components and could escape or alias the plugins root when building plugin_dir. It bails via anyhow with the offending id in the message.
Solutions
- Use ids that do not start with '.' and are not '.'/'..'
- Sanitize or reject user-supplied ids before calling plugin_dir
- Adopt the allowlist approach noted in the code (courses, study, telegram, convert, misc)
Example fix
// before
let dir = plugin_dir("..")?; // bails
// after
ensure!(!id.starts_with('.') && id != "..", "invalid plugin id");
let dir = plugin_dir(&id)?; Defensive patterns
Strategy: validation
Validate before calling
ensure!(!plugin_id.is_empty() && !plugin_id.starts_with('.') && plugin_id != "..", "invalid plugin id: {plugin_id:?}"); Type guard
fn is_valid_id_shape(id: &str) -> bool { !id.is_empty() && !id.starts_with('.') } Try / catch
let dir = plugin_dir(&id).with_context(|| format!("bad plugin id {id:?}"))?; Prevention
- Never use raw user input as directory names
- Prefer an id allowlist
- Sanitize ids before path construction
When it happens
Trigger: Passing ".", "..", or an id like ".hidden" to plugin_dir/validate_plugin_id — typically from a manifest id field or untrusted user input intended as a directory name.
Common situations: User-supplied id from a URL/query used directly as a directory name; attacker-crafted id attempting path traversal; typo'd id like "..backup".
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
- external_data_cache: plugin_id/namespace must not contain…
- 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/0d09cd25a271db74.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/plugin_loader.rs:236
/// caminho arbitrario.
pub fn plugin_dir(&self, plugin_id: &str) -> anyhow::Result<PathBuf> {
validate_plugin_id(plugin_id)?;
Ok(self.plugins_dir.join(plugin_id))
}
}
/// Aceita apenas `[A-Za-z0-9._-]`, recusando separador de caminho, byte nulo,
/// componente relativo e id vazio.
///
/// 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");View on GitHub (pinned to 8600b91f42)