{"record":{"id":"b2e2bfbcb017ed6c","repo":"alibaba/open-code-review","slug":"file-path-q-is-outside-repository-b2e2bf","errorCode":null,"errorMessage":"file path %q is outside repository","messagePattern":"file path %q is outside repository","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tool/filereader.go","lineNumber":102,"sourceCode":"\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tcontent, err := os.ReadFile(fullPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read file %q: %w\", path, err)\n\t}\n\treturn string(content), nil\n}\n\nfunc (fr *FileReader) resolveWorkspacePath(path string) (string, error) {\n\trepoRoot, err := pathutil.CanonicalPath(fr.RepoDir)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"resolve repository path %q: %w\", fr.RepoDir, err)\n\t}\n\n\tfullPath := filepath.Join(repoRoot, path)\n\tif !pathutil.WithinBase(repoRoot, fullPath) {\n\t\treturn \"\", fmt.Errorf(\"file path %q is outside repository\", path)\n\t}\n\n\tresolvedPath, err := filepath.EvalSymlinks(fullPath)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn fullPath, nil\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"resolve file %q: %w\", path, err)\n\t}\n\tif !pathutil.WithinBase(repoRoot, resolvedPath) {\n\t\treturn \"\", fmt.Errorf(\"file path %q is outside repository\", path)\n\t}\n\treturn resolvedPath, nil\n}\n\nfunc (fr *FileReader) readFromGitShow(parentCtx context.Context, path string) (string, error) {\n\tctx, cancel := context.WithTimeout(parentCtx, 30*time.Second)\n\tdefer cancel()","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/tool/filereader.go#L84-L120","documentation":"Security guard error from FileReader.resolveWorkspacePath: after joining the path onto the repository root (and again after symlink resolution), pathutil.WithinBase determines the resulting path is not contained within the repository. This is a path-traversal containment check, so requests like '../secrets' or symlinked escapes are rejected with 'file path %q is outside repository'.","triggerScenarios":"Calling Read/ReadLines in workspace mode with a path containing ../ segments that escape the repo root, an absolute path treated as an escape, or a symlink inside the repo pointing to a target outside the repository.","commonSituations":"Agent attempting to read /etc/passwd via traversal; a repository containing symlinks to shared libraries outside the tree; test fixtures joining user-supplied paths onto the root without sanitizing.","solutions":["Pass a repository-relative path without ../ components (filepath.Rel against the repo root helps).","For symlinked files, either move the real file inside the repository or read it via the git ref modes (range/commit), which use git show instead of the filesystem.","Sanitize/normalize user-supplied paths before handing them to the tool and check they stay under the repo root."],"exampleFix":"// before: escapes the repository\nfr.Read(ctx, \"../../etc/passwd\")\n// after: repo-relative path\nfr.Read(ctx, \"internal/tool/filereader.go\")","handlingStrategy":"validation","validationCode":"abs, _ := filepath.Abs(filepath.Join(repoRoot, userInput))\nif !strings.HasPrefix(abs, repoRoot+string(os.PathSeparator)) {\n    return fmt.Errorf(\"refusing path escaping repo: %s\", userInput)\n}","typeGuard":"func containedIn(base, p string) bool { abs, err := filepath.Abs(p); if err != nil { return false }; rel, err := filepath.Rel(base, abs); return err == nil && rel != \"..\" && !strings.HasPrefix(rel, \"..\"+string(os.PathSeparator)) }","tryCatchPattern":"content, err := fr.Read(ctx, rel)\nif err != nil && strings.Contains(err.Error(), \"outside repository\") {\n    log.Warn(\"path traversal attempt blocked; using repo-relative fallback\")\n    return fr.Read(ctx, filepath.FromSlash(sanitizedRel))\n}","preventionTips":["Always sanitize user/LLM-supplied paths: strip leading '/', collapse '..', and verify containment against the repo root.","Treat this error as a security signal — log the offending path and caller.","Audit repositories for symlinks pointing outside the tree before enabling workspace reads.","Use filepath.Rel to normalize any path onto the repo root before calling Read."],"tags":["security","path-traversal","validation","sandbox"],"backgroundTag":"path-traversal-blocked","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}