{"record":{"id":"1a687d73e64eb4bc","repo":"gastownhall/beads","slug":"beads-dir-points-to-unsafe-location-s","errorCode":null,"errorMessage":"BEADS_DIR points to unsafe location: %s","messagePattern":"BEADS_DIR points to unsafe location: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/beads/context.go","lineNumber":113,"sourceCode":"func GetRepoContext() (*RepoContext, error) {\n\trepoCtxOnce.Do(func() {\n\t\trepoCtx, repoCtxErr = buildRepoContext()\n\t})\n\treturn repoCtx, repoCtxErr\n}\n\n// buildRepoContext constructs the RepoContext by resolving all paths.\n// This is called once per process via sync.Once.\nfunc buildRepoContext() (*RepoContext, error) {\n\t// 1. Find .beads directory (respects BEADS_DIR env var)\n\tbeadsDir := FindBeadsDir()\n\tif beadsDir == \"\" {\n\t\treturn nil, fmt.Errorf(\"no .beads directory found\")\n\t}\n\n\t// 2. Security: Validate path boundary (SEC-003)\n\tif !isPathInSafeBoundary(beadsDir) {\n\t\treturn nil, fmt.Errorf(\"BEADS_DIR points to unsafe location: %s\", beadsDir)\n\t}\n\n\t// 3. Check for redirect file in the local repo\n\tredirectInfo := GetRedirectInfo()\n\n\t// 4. Determine RepoRoot based on external/redirect status\n\tvar repoRoot string\n\tisExternal := redirectInfo.IsRedirected\n\tif !isExternal {\n\t\tif external, err := isExternalBeadsDir(beadsDir); err == nil {\n\t\t\tisExternal = external\n\t\t}\n\t}\n\n\tif isExternal {\n\t\t// Beads dir is in a different repo - use that repo's root\n\t\trepoRoot = repoRootForBeadsDir(beadsDir)\n\t} else {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/beads/context.go#L95-L131","documentation":"As a security check (SEC-003), buildRepoContext rejects a resolved beadsDir that fails isPathInSafeBoundary and returns \"BEADS_DIR points to unsafe location: %s\". This prevents BEADS_DIR from redirecting beads storage to dangerous locations such as system paths, paths outside the repo boundary, or symlinked escapes. The error names the offending resolved path so you can see exactly what was rejected.","triggerScenarios":"Setting BEADS_DIR to /, /etc, $HOME, /tmp or another sensitive/system path; a BEADS_DIR whose canonical path resolves outside the allowed boundary via symlinks; relative paths that resolve unexpectedly; hostile or corrupted environment inherited from a parent process.","commonSituations":"Exporting BEADS_DIR globally in a shell profile and later running bd in repos where that path is unsafe; CI environments injecting BEADS_DIR for all jobs; developers experimenting with shared beads directories pointing at system locations.","solutions":["Unset BEADS_DIR (or set it to a real .beads directory inside a repository) and retry.","Resolve the printed path and check for symlinks: use `readlink -f` to see the canonical location.","Point BEADS_DIR at a directory within a repository you control, not system/shared paths.","Audit shell profiles and CI config for stray BEADS_DIR exports.","Restart the process after fixing — the resolved context is cached via sync.Once."],"exampleFix":"// before\nexport BEADS_DIR=/tmp/shared-beads\nbd doctor // \"BEADS_DIR points to unsafe location: /tmp/shared-beads\"\n// after\nunset BEADS_DIR\n# or: export BEADS_DIR=/path/to/repo/.beads\nbd doctor","handlingStrategy":"validation","validationCode":"if dir := os.Getenv(\"BEADS_DIR\"); dir != \"\" {\n    abs, _ := filepath.Abs(dir)\n    for _, bad := range []string{\"/\", \"/etc\", \"/usr\", \"/var\", os.Getenv(\"HOME\")} {\n        if abs == bad { return fmt.Errorf(\"BEADS_DIR %s is unsafe\", abs) }\n    }\n}","typeGuard":null,"tryCatchPattern":"if _, err := beads.GetRepoContext(); err != nil {\n    if strings.Contains(err.Error(), \"BEADS_DIR points to unsafe location\") {\n        return fmt.Errorf(\"fix or unset BEADS_DIR (%v)\", err)\n    }\n    return err\n}","preventionTips":["Never point BEADS_DIR at system, home, or temp root paths.","Set BEADS_DIR only per-project (direnv, CI job env), not globally in shell profiles.","Resolve symlinks before choosing a BEADS_DIR target.","Audit CI configuration for inherited BEADS_DIR values."],"tags":["go","security","environment-variable","path-validation","config"],"backgroundTag":"unsafe-path","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}