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

  1. Sanitize key names before constructing a Key: reject '..', absolute segments, and characters outside the valid set
  2. Use a canonical, absolute base_path (fs::canonicalize) so starts_with checks are meaningful
  3. Log the offending key name and remove or migrate it from the store/input source
  4. 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

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)