{"record":{"id":"e1c6c700e0eb64e9","repo":"GopeedLab/gopeed","slug":"download-directory-is-not-in-white-list","errorCode":null,"errorMessage":"download directory is not in white list","messagePattern":"download directory is not in white list","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/downloader.go","lineNumber":1383,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\topts.Path = storeConfig.DownloadDir\n\t}\n\t// Replace placeholders in download path (e.g., %year%, %month%, %day%, %date%)\n\topts.Path = util.ReplacePathPlaceholders(opts.Path)\n\n\t// if enable white download directory, check if the download directory is in the white list\n\tif len(d.cfg.WhiteDownloadDirs) > 0 {\n\t\tinWhiteList := false\n\t\tfor _, dir := range d.cfg.WhiteDownloadDirs {\n\t\t\tif match, err := filepath.Match(dir, opts.Path); match && err == nil {\n\t\t\t\tinWhiteList = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !inWhiteList {\n\t\t\treturn nil, errors.New(\"download directory is not in white list\")\n\t\t}\n\t}\n\treturn opts, nil\n}\n\nfunc (d *Downloader) statusMut(task *Task, fn func() (bool, error)) (bool, error) {\n\ttask.statusLock.Lock()\n\tdefer task.statusLock.Unlock()\n\n\treturn fn()\n}\n\nfunc (d *Downloader) doStart(task *Task) (err error) {\n\tvar isCreate bool\n\tvar generation uint64\n\tisReturn, err := d.statusMut(task, func() (isReturn bool, err error) {\n\t\tif task.Status == base.DownloadStatusRunning || task.Status == base.DownloadStatusDone {\n\t\t\tisReturn = true","sourceCodeStart":1365,"sourceCodeEnd":1401,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/downloader.go#L1365-L1401","documentation":"In Downloader.initOptions (pkg/download/downloader.go, ~1360-1389), after path placeholders are replaced, if the config has a non-empty WhiteDownloadDirs list the final opts.Path must match at least one entry via filepath.Match(dir, opts.Path), otherwise creation fails with 'download directory is not in white list'. This is a security feature confining downloads to approved directories.","triggerScenarios":"WhiteDownloadDirs is configured (e.g. [\"/data/downloads/*\"]) and a task is created with opts.Path = \"/etc\" or any non-matching directory; the path placeholder ({temp-dir} etc. via util.ReplacePathPlaceholders) resolves to a directory outside the whitelist; pattern semantics mismatch — filepath.Match treats '*' as not crossing '/' and has no '**', so /data/* won't match /data/a/b; case/separator differences (Windows \\ vs /) between config and opts.Path.","commonSituations":"Server deployments enabling the whitelist but clients sending absolute paths; upgrading a deployment where the whitelist was added but old clients keep their previous paths; users assuming glob '**' support; relative paths in opts.Path that never match an absolute whitelist entry.","solutions":["Set the task's opts.Path to a whitelisted directory (or leave it empty to use the configured default download dir, which is normally whitelisted)","Fix the whitelist pattern to actually cover the path: use /data/downloads/* for one level or list each subdirectory; remember filepath.Match '*' does not cross '/'","Verify with the same matcher before creating: filepath.Match(pattern, resolvedPath)","For deployments, whitelist the base download directory and derive per-task subpaths under it"],"exampleFix":"// before\ncfg.WhiteDownloadDirs = []string{\"/srv/dl/*\"}\n_, err := d.CreateDirect(req, &base.Options{Path: \"/srv/downloads/file.iso\"}) // no match\n\n// after\ncfg.WhiteDownloadDirs = []string{\"/srv/dl/*\", \"/srv/downloads/*\"}\n// or point the task inside an allowed dir:\n_, err := d.CreateDirect(req, &base.Options{Path: \"/srv/dl/file.iso\"})","handlingStrategy":"validation","validationCode":"func pathAllowed(whiteDirs []string, p string) bool {\n    for _, d := range whiteDirs {\n        if ok, err := filepath.Match(d, p); ok && err == nil { return true }\n    }\n    return false\n}\nresolved := util.ReplacePathPlaceholders(opts.Path)\nif len(cfg.WhiteDownloadDirs) > 0 && !pathAllowed(cfg.WhiteDownloadDirs, resolved) {\n    return fmt.Errorf(\"path %q not in white list\", resolved)\n}","typeGuard":null,"tryCatchPattern":"taskId, err := downloader.CreateDirect(req, opts)\nif err != nil && strings.Contains(err.Error(), \"not in white list\") {\n    // show config guidance: whitelist the directory or choose an allowed path\n}","preventionTips":["Derive every task path under the whitelisted base download directory","Whitelist entries must match filepath.Match semantics: '*' does not cross '/', no '**'","Apply ReplacePathPlaceholders before comparing, exactly like initOptions does"],"tags":["config","security","path","whitelist"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}