{"record":{"id":"3c24a07237f8e4d9","repo":"siyuan-note/siyuan","slug":"path-escapes-workspace-s","errorCode":null,"errorMessage":"path escapes workspace: %s","messagePattern":"path escapes workspace: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/cli/cmd/file.go","lineNumber":44,"sourceCode":"\t\"text/tabwriter\"\n\n\t\"github.com/88250/gulu\"\n\t\"github.com/siyuan-note/siyuan/kernel/model\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nvar fileCmd = &cobra.Command{\n\tUse:   \"file\",\n\tShort: \"Workspace file operations\",\n}\n\nfunc absPath(rel string) (string, error) {\n\trel = filepath.Clean(strings.ReplaceAll(rel, \"/\", string(os.PathSeparator)))\n\tabs := filepath.Join(util.WorkspaceDir, rel)\n\tif !gulu.File.IsSubPath(util.WorkspaceDir, abs) {\n\t\treturn \"\", fmt.Errorf(\"path escapes workspace: %s\", rel)\n\t}\n\tif boxID := model.EncryptedRawPathBoxID(abs); boxID != \"\" {\n\t\treturn \"\", fmt.Errorf(\"path belongs to encrypted notebook [%s]: %s\", boxID, rel)\n\t}\n\treturn abs, nil\n}\n\nvar fileListCmd = &cobra.Command{\n\tUse:   \"list <path>\",\n\tShort: \"List directory contents\",\n\tArgs:  cobra.MinimumNArgs(1),\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\tdir, err := absPath(args[0])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tentries, err := os.ReadDir(dir)\n\t\tif err != nil {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/cli/cmd/file.go#L26-L62","documentation":"absPath resolves a user-supplied relative path against the SiYuan workspace directory and refuses paths that would resolve outside it. It cleans the path, joins it onto util.WorkspaceDir, and verifies containment with gulu.File.IsSubPath; on violation it returns this error naming the offending relative path. This is a path-traversal defense protecting workspace files.","triggerScenarios":"Passing a path containing `..` segments (e.g. `../../etc/passwd`) or an absolute path outside the workspace to CLI file commands such as `siyuan file list <path>` / read / write.","commonSituations":"Scripting file operations with paths built from external input; typos with extra `..`; attempting to read files adjacent to the workspace; automated tools given paths in the wrong root.","solutions":["Use paths relative to the workspace root without `..` escape segments (e.g. `notebooks/<box>/doc.sy`).","Resolve the intended file inside the workspace and re-run the command.","If you need a file outside the workspace, copy it in first or use the appropriate non-workspace-scoped tooling.","Sanitize/normalize user input in scripts before passing it to the CLI."],"exampleFix":"// before\nsiyuan file list ../../secrets\n// error: path escapes workspace: ../../secrets\n\n// after\nsiyuan file list notebooks/20240101120000-abc1234/20240501120000-xyz9876.sy","handlingStrategy":"validation","validationCode":"// Go caller-side check before invoking CLI file commands\nrel := filepath.ToSlash(filepath.Clean(userPath))\nif strings.HasPrefix(rel, \"../\") || rel == \"..\" || filepath.IsAbs(userPath) {\n    return fmt.Errorf(\"refusing non-workspace-relative path: %s\", rel)\n}","typeGuard":"func safeWorkspaceRel(p string) (string, bool) {\n    cleaned := filepath.ToSlash(filepath.Clean(p))\n    if filepath.IsAbs(p) || cleaned == \"..\" || strings.HasPrefix(cleaned, \"../\") {\n        return \"\", false\n    }\n    return cleaned, true\n}","tryCatchPattern":"abs, err := absPath(rel)\nif err != nil {\n    if strings.Contains(err.Error(), \"path escapes workspace\") {\n        return fmt.Errorf(\"refusing path outside workspace: %s\", rel)\n    }\n    return err\n}","preventionTips":["Always pass workspace-relative paths without .. segments","Sanitize externally supplied paths before passing to the CLI","Never attempt to reach files outside the workspace through file commands","Treat this error as a security signal, not a nuisance"],"tags":["security","path-traversal","cli"],"backgroundTag":"path-traversal-blocked","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}