bottlerocket-os/bottlerocket · error · datastore::Error
PathTraversalSnafu
PathTraversalSnafu
Error message
Key would traverse outside data store: {name} What it means
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).
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
Example fix
// before
let key = Key::from_string(user_input)?;
let data = store.get_key(&key)?; // panics/errors with path traversal
// after
let key = Key::from_string(user_input)?;
if key.name().contains("..") || key.name().starts_with('/') {
return Err(format!("rejected unsafe key: {}", key.name()).into());
}
let data = store.get_key(&key)?; Defensive patterns
Strategy: validation
Validate before calling
fn safe(key: &str) -> bool { !key.contains("..") && !key.starts_with('/') } Prevention
- Sanitize keys before use
When it happens
Trigger: 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.
Common situations: 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.
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 bottlerocket-os/bottlerocket@0be31b34d2 (2026-09-10).
Data as JSON: /api/errors/ccbf02b499f9b9b6.
Report an issue: GitHub.
Appendix: source
Thrown at sources/api/datastore/src/filesystem.rs:71
}
/// Returns the appropriate path on the filesystem for the given data key.
fn data_path(&self, key: &Key, committed: &Committed) -> Result<PathBuf> {
let base_path = self.base_path(committed);
// Encode key segments so they're filesystem-safe
let encoded: Vec<_> = key.segments().iter().map(encode_path_component).collect();
// Join segments with filesystem separator to get path underneath data store
let path_suffix = encoded.join(path::MAIN_SEPARATOR_STR);
// Make path from base + prefix
// FIXME: canonicalize requires that the full path exists. We know our Key is checked
// for acceptable characters, so join should be safe enough, but come back to this.
// let path = fs::canonicalize(self.base_path.join(path_suffix))?;
let path = base_path.join(path_suffix);
// Confirm no path traversal outside of base
ensure!(
path != *base_path && path.starts_with(base_path),
error::PathTraversalSnafu { name: key.name() }
);
Ok(path)
}
/// Returns the appropriate path on the filesystem for the given metadata key.
fn metadata_path(
&self,
metadata_key: &Key,
data_key: &Key,
committed: &Committed,
) -> Result<PathBuf> {
let path = self.data_path(data_key, committed)?;
// We want to add to the existing file name, not create new path components (directories),
// so we use a string type rather than a path type.View on GitHub (pinned to 0be31b34d2)