{"record":{"id":"923a2d330817fe99","repo":"docker/cli","slug":"path-q-is-outside-of-root-config-directory-q","errorCode":null,"errorMessage":"path %q is outside of root config directory %q","messagePattern":"path %q is outside of root config directory %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/config/config.go","lineNumber":103,"sourceCode":"}\n\n// ContextStoreDir returns the directory the docker contexts are stored in\nfunc ContextStoreDir() string {\n\treturn filepath.Join(Dir(), contextsDir)\n}\n\n// SetDir sets the directory the configuration file is stored in\nfunc SetDir(dir string) {\n\t// trigger the sync.Once to synchronise with Dir()\n\tinitConfigDir.Do(func() {})\n\tconfigDir = filepath.Clean(dir)\n}\n\n// Path returns the path to a file relative to the config dir\nfunc Path(p ...string) (string, error) {\n\tpath := filepath.Join(append([]string{Dir()}, p...)...)\n\tif !strings.HasPrefix(path, Dir()+string(filepath.Separator)) {\n\t\treturn \"\", fmt.Errorf(\"path %q is outside of root config directory %q\", path, Dir())\n\t}\n\treturn path, nil\n}\n\n// LoadFromReader is a convenience function that creates a ConfigFile object from\n// a reader. It returns an error if configData is malformed.\nfunc LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {\n\tconfigFile := configfile.ConfigFile{\n\t\tAuthConfigs: make(map[string]types.AuthConfig),\n\t}\n\terr := configFile.LoadFromReader(configData)\n\treturn &configFile, err\n}\n\n// Load reads the configuration file ([ConfigFileName]) from the given directory.\n// If no directory is given, it uses the default [Dir]. A [*configfile.ConfigFile]\n// is returned containing the contents of the configuration file, or a default\n// struct if no configfile exists in the given location.","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/config/config.go#L85-L121","documentation":"Security guard in config.Path (config.go:100-104) that rejects any resolved path which escapes the Docker config directory. Path joins the config dir with the caller-supplied segments, then verifies the result still has the config dir as a prefix (with separator). If not, the segments contained `..` traversal or an absolute component that redirected outside the dir.","triggerScenarios":"Calling config.Path(p...) where any element in p contains `..` that climbs above the config dir, or an absolute path component (e.g. \"/etc/passwd\") that, after filepath.Join+Clean, no longer starts with `Dir()+separator`.","commonSituations":"A tool or context-meta path is built from user/remote input and an attacker (or a misconfigured context) supplies `../../../etc/something`. Also seen when a path segment is inadvertently absolute, overriding the join base. This is a path-traversal protection, not normal usage.","solutions":["Sanitize path segments before passing them to config.Path: reject or strip leading slashes and `..` components.","Use filepath.Clean on each segment and verify it does not start with `..` before calling Path.","Construct the full path yourself and confirm it is under Dir() if you cannot control the input."],"exampleFix":"// before\np, err := config.Path(userInput)\n// after — sanitize first\ncleaned := filepath.Clean(\"/\" + userInput) // force relative\nif strings.HasPrefix(cleaned, \"..\") {\n    return fmt.Errorf(\"invalid path\")\n}\np, err := config.Path(cleaned)","handlingStrategy":"validation","validationCode":"// Reject traversal before calling config.Path.\nfunc safeSegment(s string) error {\n    s = filepath.Clean(s)\n    if strings.HasPrefix(s, \"..\") || filepath.IsAbs(s) {\n        return fmt.Errorf(\"unsafe path segment: %q\", s)\n    }\n    return nil\n}\nfor _, seg := range segments {\n    if err := safeSegment(seg); err != nil {\n        return err\n    }\n}","typeGuard":null,"tryCatchPattern":"p, err := config.Path(segments...)\nif err != nil {\n    return fmt.Errorf(\"rejected unsafe path: %w\", err)\n}","preventionTips":["Never pass untrusted user input straight to config.Path; sanitize first.","Treat absolute paths and `..` in segments as invalid by policy.","Prefer building paths from a fixed allow-list of names."],"tags":["config","path-traversal","security","validation"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}