{"record":{"id":"3fdc2b5554191b84","repo":"nikivdev/code","slug":"relative-path-must-not-contain","errorCode":null,"errorMessage":"Relative path must not contain '..'.","messagePattern":"Relative path must not contain '\\.\\.'\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/code.rs","lineNumber":812,"sourceCode":"\nfn normalize_path(path: &str) -> Result<PathBuf> {\n    let expanded = config::expand_path(path);\n    let canonical = expanded.canonicalize().unwrap_or(expanded);\n    Ok(canonical)\n}\n\nfn normalize_relative_path(path: &str) -> Result<PathBuf> {\n    let trimmed = path.trim();\n    if trimmed.is_empty() {\n        bail!(\"Relative path cannot be empty.\");\n    }\n    let rel = PathBuf::from(trimmed);\n    if rel.is_absolute() {\n        bail!(\"Relative path must not be absolute.\");\n    }\n    for component in rel.components() {\n        if matches!(component, std::path::Component::ParentDir) {\n            bail!(\"Relative path must not contain '..'.\");\n        }\n    }\n    Ok(rel)\n}\n\nfn move_dir(from: &Path, to: &Path) -> Result<()> {\n    match fs::rename(from, to) {\n        Ok(()) => Ok(()),\n        Err(err) => {\n            if is_cross_device(&err) {\n                copy_dir_all(from, to)?;\n                fs::remove_dir_all(from)\n                    .with_context(|| format!(\"failed to remove {}\", from.display()))?;\n                Ok(())\n            } else {\n                Err(err).with_context(|| {\n                    format!(\"failed to move {} to {}\", from.display(), to.display())\n                })","sourceCodeStart":794,"sourceCodeEnd":830,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/code.rs#L794-L830","documentation":"normalize_relative_path walks the path's components and rejects any ParentDir component, i.e. any `..` segment. Parent-directory traversal would let a crafted path escape the project root and operate on arbitrary locations, so the function refuses it as a path-traversal defense rather than resolving it lexically.","triggerScenarios":"Calling new_project or migrate_project with a relative path containing `..`, e.g. `\"../other-project\"` or `\"a/../../b\"`.","commonSituations":"Users trying to place a project next to (rather than under) the root; scripts computing relative paths with `..`; untrusted input (web forms, generated configs) containing traversal sequences — deliberate or accidental.","solutions":["Rephrase the destination as a path strictly under the root with no `..`, e.g. `\"projects/foo\"`.","If the target truly must be outside the root, use the absolute-path API/flag instead of the relative one, if available.","Sanitize incoming user input: reject or strip `..` segments before passing the value through.","Compute sibling/parent destinations at the call site rather than encoding them in the relative argument."],"exampleFix":"// before\nnormalize_relative_path(\"../neighbor-project\")\n// after\nnormalize_relative_path(\"neighbor-project\") // lives under the tool's project root","handlingStrategy":"validation","validationCode":"let rel = Path::new(input.trim());\nlet has_parent = rel.components()\n    .any(|c| matches!(c, std::path::Component::ParentDir));\nif has_parent {\n    return Err(\"path must not contain '..' segments\".into());\n}","typeGuard":"fn is_safe_relative(s: &str) -> bool {\n    let p = Path::new(s.trim());\n    p.is_relative() && !p.components().any(|c| matches!(c, std::path::Component::ParentDir))\n}","tryCatchPattern":"match new_project(&rel) {\n    Err(e) if e.to_string().contains(\"must not contain '..'\") => {\n        eprintln!(\"Place the project under the root; '..' traversal is not allowed.\");\n    }\n    other => other?,\n}","preventionTips":["Sanitize any user-supplied path input for '..' before use.","Treat this as a security invariant for untrusted input (forms, APIs).","Compute sibling/parent destinations outside the relative-path argument."],"tags":["path-validation","path-traversal","security","input-validation"],"backgroundTag":"path-traversal-detected","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}