{"record":{"id":"2bd0cdf32a3ea42c","repo":"siyuan-note/siyuan","slug":"resource-path-s-is-not-in-workspace","errorCode":null,"errorMessage":"resource path [%s] is not in workspace","messagePattern":"resource path \\[(.+?)\\] is not in workspace","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/export.go","lineNumber":881,"sourceCode":"\tzipFilePath := filepath.Join(exportBasePath, exportID+\"-\"+zipFileName)\n\tif err = os.MkdirAll(exportFolderPath, 0755); err != nil {\n\t\tlogging.LogErrorf(\"create export temp folder failed: %s\", err)\n\t\treturn\n\t}\n\tdefer func() {\n\t\tos.RemoveAll(exportFolderPath)\n\t\tif err != nil {\n\t\t\tos.Remove(zipFilePath)\n\t\t\tos.Remove(zipFilePath + \".partial\")\n\t\t}\n\t}()\n\n\t// 将需要导出的文件/文件夹复制到临时文件夹\n\tfor _, resourcePath := range resourcePaths {\n\t\tresourceFullPath := filepath.Join(util.WorkspaceDir, resourcePath) // 资源完整路径\n\t\tif !util.IsAbsPathInWorkspace(resourceFullPath) {\n\t\t\tlogging.LogErrorf(\"resource path [%s] is not in workspace\", resourceFullPath)\n\t\t\terr = errors.New(\"resource path [\" + resourcePath + \"] is not in workspace\")\n\t\t\treturn\n\t\t}\n\n\t\tresourceBaseName := filepath.Base(resourceFullPath)                   // 资源名称\n\t\tresourceCopyPath := filepath.Join(exportFolderPath, resourceBaseName) // 资源副本完整路径\n\t\tif err = copyExportResource(resourceFullPath, resourceCopyPath); err != nil {\n\t\t\tlogging.LogErrorf(\"copy resource will be exported from [%s] to [%s] failed: %s\", resourcePath, resourceCopyPath, err)\n\t\t\terr = fmt.Errorf(Conf.Language(14), err.Error())\n\t\t\treturn\n\t\t}\n\t}\n\n\tzipPartialPath := zipFilePath + \".partial\"\n\tzip, err := gulu.Zip.Create(zipPartialPath)\n\tif err != nil {\n\t\tlogging.LogErrorf(\"create export zip [%s] failed: %s\", zipFilePath, err)\n\t\treturn\n\t}","sourceCodeStart":863,"sourceCodeEnd":899,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/export.go#L863-L899","documentation":"ExportResources validates that every requested resource path resolves to an absolute path inside the workspace. If filepath.Join(util.WorkspaceDir, resourcePath) escapes the workspace, it logs and returns 'resource path [%s] is not in workspace' — a workspace-escape guard against arbitrary file reads.","triggerScenarios":"Passing relative paths that traverse outside the workspace (e.g. '../..' segments), absolute-ish resource paths, or paths from another workspace into ExportResources.","commonSituations":"Scripting exports from user-supplied paths; callers concatenating external paths; workspace relocated so cached relative paths no longer match.","solutions":["Pass resource paths relative to the workspace data dir (e.g. 'assets/foo.png', 'notebooks/...') without '..' segments","Sanitize input with filepath.Clean and reject paths containing '..' before calling","Verify with util.IsAbsPathInWorkspace(filepath.Join(util.WorkspaceDir, p)) in the caller"],"exampleFix":"// before\nmodel.ExportResources([]string{\"/etc/passwd\"})\n// after\np := filepath.Clean(\"assets/img.png\") // relative, inside workspace\nmodel.ExportResources([]string{p})","handlingStrategy":"validation","validationCode":"func safeResource(p string) bool {\n    full := filepath.Join(util.WorkspaceDir, p)\n    return util.IsAbsPathInWorkspace(full) && !strings.Contains(filepath.Clean(p), \"..\")\n}","typeGuard":"func inWorkspace(p string) bool {\n    return util.IsAbsPathInWorkspace(filepath.Join(util.WorkspaceDir, p))\n}","tryCatchPattern":"if _, err := model.ExportResources(paths); err != nil {\n    // path escape: filter paths with safeResource and retry\n}","preventionTips":["Only pass paths relative to the workspace data dir","Reject '..' segments and absolute paths from user input","Resolve and re-check paths after any workspace relocation"],"tags":["path-validation","export","security"],"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"}