{"record":{"id":"ccbf02b499f9b9b6","repo":"bottlerocket-os/bottlerocket","slug":"pathtraversalsnafu","errorCode":"PathTraversalSnafu","errorMessage":"Key would traverse outside data store: {name}","messagePattern":"Key would traverse outside data store: (.+?)","errorType":"error_code","errorClass":"datastore::Error","httpStatus":null,"severity":"error","filePath":"sources/api/datastore/src/filesystem.rs","lineNumber":71,"sourceCode":"    }\n\n    /// Returns the appropriate path on the filesystem for the given data key.\n    fn data_path(&self, key: &Key, committed: &Committed) -> Result<PathBuf> {\n        let base_path = self.base_path(committed);\n\n        // Encode key segments so they're filesystem-safe\n        let encoded: Vec<_> = key.segments().iter().map(encode_path_component).collect();\n        // Join segments with filesystem separator to get path underneath data store\n        let path_suffix = encoded.join(path::MAIN_SEPARATOR_STR);\n\n        // Make path from base + prefix\n        // FIXME: canonicalize requires that the full path exists.  We know our Key is checked\n        // for acceptable characters, so join should be safe enough, but come back to this.\n        // let path = fs::canonicalize(self.base_path.join(path_suffix))?;\n        let path = base_path.join(path_suffix);\n\n        // Confirm no path traversal outside of base\n        ensure!(\n            path != *base_path && path.starts_with(base_path),\n            error::PathTraversalSnafu { name: key.name() }\n        );\n\n        Ok(path)\n    }\n\n    /// Returns the appropriate path on the filesystem for the given metadata key.\n    fn metadata_path(\n        &self,\n        metadata_key: &Key,\n        data_key: &Key,\n        committed: &Committed,\n    ) -> Result<PathBuf> {\n        let path = self.data_path(data_key, committed)?;\n\n        // We want to add to the existing file name, not create new path components (directories),\n        // so we use a string type rather than a path type.","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/bottlerocket-os/bottlerocket/blob/0be31b34d2ff8a7558cd78b3a2e8035207f82597/sources/api/datastore/src/filesystem.rs#L53-L89","documentation":"The datastore refuses to return a data path whose resolved location is not strictly inside the configured base directory. This guards against a key name (or a base path configuration) producing a path that escapes the store, e.g. via '..' or an absolute component. It is a deliberate safety check in `data_path`, used by every key-to-path operation (metadata, get/set/unset).","triggerScenarios":"Calling get_key/set_key/unset_key/metadata_path with a key whose joined path (base_path + key suffix) equals the base path itself or does not start with base_path — e.g. a key containing '..' segments, an absolute path component, or a base_path set to a parent/relative path such that the join escapes it.","commonSituations":"Misconfigured storage root (relative base_path combined with chdir, symlinked directories), keys reconstructed from user input without sanitization, or restoring old data whose key names predate stricter character validation.","solutions":["Sanitize key names before constructing a Key: reject '..', absolute segments, and characters outside the valid set","Use a canonical, absolute base_path (fs::canonicalize) so starts_with checks are meaningful","Log the offending key name and remove or migrate it from the store/input source","If symlinks are involved, resolve them (canonicalize) before comparison or disable symlink creation in the store"],"exampleFix":"// before\nlet key = Key::from_string(user_input)?;\nlet data = store.get_key(&key)?; // panics/errors with path traversal\n// after\nlet key = Key::from_string(user_input)?;\nif key.name().contains(\"..\") || key.name().starts_with('/') {\n    return Err(format!(\"rejected unsafe key: {}\", key.name()).into());\n}\nlet data = store.get_key(&key)?;","handlingStrategy":"validation","validationCode":"fn safe(key: &str) -> bool { !key.contains(\"..\") && !key.starts_with('/') }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Sanitize keys before use"],"tags":["datastore","path-traversal","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"0be31b34d2ff8a7558cd78b3a2e8035207f82597","analyzedAt":"2026-09-10T14:35:41.363Z","contentChangedAt":"2026-09-10T14:35:41.363Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}