{"record":{"id":"5f939868197a403e","repo":"astrid-runtime/astrid","slug":"git-history-path-must-be-a-relative-in-repository","errorCode":null,"errorMessage":"git history path must be a relative in-repository path","messagePattern":"git history path must be a relative in-repository path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-chunker-evidence/src/corpus.rs","lineNumber":556,"sourceCode":"        bail!(\"corpus label must contain only lowercase ASCII letters, digits, and hyphens\");\n    }\n    Ok(())\n}\n\nfn validate_relative_git_path(path: &Path) -> Result<()> {\n    if path.as_os_str().is_empty()\n        || path.is_absolute()\n        || path.components().any(|component| {\n            matches!(\n                component,\n                std::path::Component::ParentDir\n                    | std::path::Component::RootDir\n                    | std::path::Component::Prefix(_)\n            )\n        })\n        || path.to_string_lossy().contains(':')\n    {\n        bail!(\"git history path must be a relative in-repository path\");\n    }\n    Ok(())\n}\n\nfn git_path_exists_at_revision(\n    repository: &Path,\n    relative_path: &Path,\n    revision: &str,\n) -> Result<bool> {\n    let tree = Command::new(\"git\")\n        .args([\"-C\"])\n        .arg(repository)\n        .args([\"ls-tree\", \"--full-tree\", revision, \"--\"])\n        .arg(relative_path)\n        .output()\n        .context(\"inspect captured version path\")?;\n    if !tree.status.success() {\n        bail!(","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-chunker-evidence/src/corpus.rs#L538-L574","documentation":"`validate_relative_git_path` rejects paths that are not clean relative paths inside a repository: absolute paths, `.`/`..`/root/prefix components, or anything containing a colon are refused. This guards `git ls-tree` invocations against path traversal and argument injection.","triggerScenarios":"Passing a `RELATIVE_PATH` (from a `--git-history NAME=REPO::PATH` spec) that is absolute, contains `..`, starts with `/` or a Windows drive prefix, or includes a `:` character into `version_chain_from_git`.","commonSituations":"Users supplying absolute paths in the git-history spec, copy-pasting `repo/sub/dir:file` notation, or attempting `../../etc/passwd` style traversal in the config.","solutions":["Use a plain relative path inside the repository, e.g. `bench/corpus/sample.bin`","Strip leading `/`, resolve/remove `..` components relative to the repo root before passing","Remove any `:` characters from the path (or rename the offending file)"],"exampleFix":"// before\nlet spec = \"samples=/srv/repo::/data/../../etc/passwd\";\n// after\nlet spec = \"samples=/srv/repo::bench/corpus/sample.bin\";","handlingStrategy":"validation","validationCode":"fn is_safe_rel_path(p: &Path) -> bool {\n    !p.as_os_str().is_empty()\n        && !p.is_absolute()\n        && p.components().all(|c| matches!(c, std::path::Component::Normal(_)))\n        && !p.to_string_lossy().contains(':')\n}\nassert!(is_safe_rel_path(&rel), \"unsafe git history path: {rel:?}\");","typeGuard":null,"tryCatchPattern":"match version_chain_from_git(&repo, &rev, &rel) {\n    Ok(chain) => chain,\n    Err(e) if e.to_string().contains(\"relative in-repository path\") => {\n        let cleaned = normalize_rel(&rel);\n        version_chain_from_git(&repo, &rev, &cleaned)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always store corpus paths relative to the repo root in configs","Reject `..`, absolute paths, and `:` in any user-supplied git path early","Run `git ls-files -- <path>` to confirm the path is tracked before using it in a spec"],"tags":["rust","validation","path","git","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}