{"record":{"id":"fde33a5f9c8e73b0","repo":"crowdsecurity/crowdsec","slug":"q-path-escapes-base-directory-q","errorCode":null,"errorMessage":"%q: path escapes base directory %q","messagePattern":"%q: path escapes base directory %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/cwhub/safepath.go","lineNumber":37,"sourceCode":"\tif filepath.IsAbs(relPath) ||\n\t\t// on windows, IsAbs fails for paths beginning with \"/\", since it's the root of the drive\n\t\tstrings.HasPrefix(relPath, string(os.PathSeparator)) ||\n\t\tstrings.HasPrefix(relPath, \"/\") {\n\t\treturn \"\", fmt.Errorf(\"%q: must be a relative path\", relPath)\n\t}\n\n\tabsFilePath, err := filepath.Abs(filepath.Join(absBase, relPath))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\trel, err := filepath.Rel(absBase, absFilePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif strings.HasPrefix(rel, \"..\") {\n\t\treturn \"\", fmt.Errorf(\"%q: path escapes base directory %q\", relPath, baseDir)\n\t}\n\n\treturn absFilePath, nil\n}\n","sourceCodeStart":19,"sourceCodeEnd":42,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/cwhub/safepath.go#L19-L42","documentation":"After computing the absolute joined path, SafePath re-derives filepath.Rel(absBase, absFilePath). If the relative result starts with \"..\", the requested path climbs outside the base directory, which is rejected to prevent path traversal (e.g. via \"../\" segments).","triggerScenarios":"SafePath called with relative paths containing \"..\" segments such as \"../../escape.yaml\" or nested like \"a/../../b.yaml\" that resolve outside absBase.","commonSituations":"Malicious hub index entries with traversal segments; buggy path construction concatenating user input; symlink-free traversal attempts during download/install of hub items.","solutions":["Clean the input with filepath.Clean and reject paths containing \"..\" before calling","Validate the item name/remote path against an allow-list pattern (no separators or dots sequences)","Log the offending input — this usually indicates tampered or malformed data, not a user fix"],"exampleFix":"// before\nSafePath(baseDir, \"../../etc/shadow\")\n// after\nrel := filepath.Clean(userPath)\nif strings.HasPrefix(rel, \"..\") { return errors.New(\"invalid path\") }\np, err := SafePath(baseDir, rel)","handlingStrategy":"validation","validationCode":"cleaned := filepath.Clean(userPath)\nif strings.HasPrefix(cleaned, \"..\") {\n    return errors.New(\"path must stay within the hub directory\")\n}","typeGuard":null,"tryCatchPattern":"p, err := cwhub.SafePath(baseDir, rel)\nif err != nil {\n    return fmt.Errorf(\"rejected unsafe path %q: %w\", rel, err) // treat as security event\n}","preventionTips":["Reject any path containing \"..\" segments at the input boundary","Log SafePath rejections as potential tampering","Keep SafePath as the single choke point for all hub file paths"],"tags":["security","path-traversal","validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}