Hmbown/CodeWhale · error · io::Error

Fleet artifact path must stay within the workspace

Error message

Fleet artifact path must stay within the workspace

What it means

This error means the resolved Fleet artifact path escaped the workspace directory or contained illegal components (e.g. a drive colon on Windows, non-Normal components). The library throws it from invalid_path, used by WorkspaceFile::open and sibling, whenever validation of the joined path fails.

Solutions

  1. Sanitize the artifact name: strip path separators and reject '..' before joining
  2. On Windows, remove or replace any ':' in the filename
  3. Join relative names onto the workspace directory and verify the result stays within it before calling open

Example fix

// before
let name = format!("{}.json", user_input);
// after
let name = format!("{}.json", user_input.replace(['/', '\\', ':'], "_"));
Defensive patterns

Strategy: validation

Validate before calling

fn safe_artifact_name(name: &str) -> Option<&str> {
    let ok = !name.is_empty()
        && !name.contains('..')
        && !name.contains('/')
        && !name.contains('\\')
        && !(cfg!(windows) && name.contains(':'));
    ok.then_some(name)
}

Try / catch

match WorkspaceFile::open(dir, name, write) {
    Ok(f) => use_file(f),
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
        eprintln!("artifact name escaped workspace or contains illegal characters");
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Passing a filename containing '..' or absolute components, or on Windows a name containing a ':' (alternate data stream / drive letter), to Fleet artifact open/sibling APIs.

Common situations: Building artifact names from untrusted or user-supplied input containing path separators or '..'; Windows filenames with a colon intended as a timestamp; misconfigured artifact directory.

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 Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/fb016bebb2757a76. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/files.rs:16

//! Workspace-confined file operations shared by Fleet artifacts and its ledger.

use std::fs::File;
use std::io::{self, Write};
use std::path::{Component, Path};

pub(crate) fn path_is_confined(path: &Path) -> bool {
    !path.as_os_str().is_empty()
        && path.components().all(|component| match component {
            Component::Normal(name) => !cfg!(windows) || !name.as_encoded_bytes().contains(&b':'),
            _ => false,
        })
}

fn invalid_path() -> io::Error {
    io::Error::new(
        io::ErrorKind::InvalidInput,
        "Fleet artifact path must stay within the workspace",
    )
}

#[cfg(unix)]
#[derive(Debug)]
pub(crate) struct WorkspaceFile {
    directory: File,
    filename: std::ffi::CString,
}

#[cfg(unix)]
impl WorkspaceFile {
    pub(crate) fn open(workspace: &Path, relative: &Path, create: bool) -> io::Result<Self> {
        use std::os::fd::{AsRawFd, FromRawFd};
        use std::os::unix::ffi::OsStrExt;
        if !path_is_confined(relative) {

View on GitHub (pinned to 73e0f67d83)