{"record":{"id":"e2e7e00e3d825473","repo":"influxdata/influxdb","slug":"path-traversal-detected-plugin-filename-0-attempts-to-access","errorCode":null,"errorMessage":"Path traversal detected: plugin filename '{0}' attempts to access files outside the plugin directory","messagePattern":"Path traversal detected: plugin filename '(.+?)' attempts to access files outside the plugin directory","errorType":"validation","errorClass":"PluginError","httpStatus":null,"severity":"error","filePath":"influxdb3_processing_engine/src/plugins.rs","lineNumber":79,"sourceCode":"\n    #[error(\"non-schedule plugin with schedule trigger: {0}\")]\n    NonSchedulePluginWithScheduleTrigger(String),\n\n    #[error(\"error fetching plugin from repository: {0} {1}\")]\n    FetchingFromRepository(reqwest::StatusCode, String),\n\n    #[error(\n        \"plugin installation is disabled; plugins must already exist in the configured plugin directory\"\n    )]\n    PluginInstallationDisabled,\n\n    #[error(\"Join error, please report: {0}\")]\n    JoinError(#[from] tokio::task::JoinError),\n\n    #[error(\"Node not configured with plugin directory\")]\n    NoPluginDir,\n\n    #[error(\n        \"Path traversal detected: plugin filename '{0}' attempts to access files outside the plugin directory\"\n    )]\n    PathTraversal(String),\n}\n\n#[derive(Debug, Clone)]\npub struct ProcessingEngineEnvironmentManager {\n    pub plugin_dir: Option<PathBuf>,\n    pub virtual_env_location: Option<PathBuf>,\n    pub package_manager: Arc<dyn PythonEnvironmentManager>,\n    pub plugin_dir_only: bool,\n    pub plugin_repo: Option<String>,\n}\n\npub(crate) fn run_schedule_event_source(\n    trigger_definition: Arc<TriggerDefinition>,\n    time_provider: Arc<dyn TimeProvider>,\n    scheduler: Scheduler,","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_processing_engine/src/plugins.rs#L61-L97","documentation":"PluginError::PathTraversal is thrown when a requested plugin filename escapes the configured plugin directory (contains '..' segments or absolute paths), a security guard against arbitrary file reads/writes via the plugin API.","triggerScenarios":"Requesting a plugin whose filename resolves outside the plugin dir, e.g. '../../etc/passwd' or '/etc/cron.d/evil' passed to the install/lookup API.","commonSituations":"Malicious or mistaken API calls using relative paths; automation scripts constructing filenames from untrusted input; copy-pasted paths with directory components.","solutions":["Pass only a bare filename with no path separators or '..' segments.","Sanitize/normalize the name (keep alphanumerics, dot, underscore, hyphen) before calling the API.","Place required files inside the plugin directory and reference them by name only."],"exampleFix":"// before\nclient.install_plugin(\"../../etc/passwd\")\n// after\nclient.install_plugin(\"my_plugin.py\")","handlingStrategy":"validation","validationCode":"fn is_safe_plugin_name(name: &str) -> bool {\n    !name.is_empty()\n        && !name.contains(['/', '\\\\'])\n        && !name.contains(\"..\")\n        && name.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-'))\n}","typeGuard":"fn is_bare_filename(name: &str) -> bool { name == std::path::Path::new(name).file_name().and_then(|s| s.to_str()).unwrap_or(\"\") }","tryCatchPattern":null,"preventionTips":["Never build plugin names from untrusted user input.","Whitelist filenames against the plugin directory listing.","Treat any path-separator in a plugin name as an error in callers."],"tags":["security","path-traversal","plugins"],"backgroundTag":"path-traversal-blocked","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}