{"record":{"id":"4c91a9733060ae47","repo":"siyuan-note/siyuan","slug":"export-path-is-outside-export-directory","errorCode":null,"errorMessage":"export path is outside export directory","messagePattern":"export path is outside export directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/encrypted_export.go","lineNumber":219,"sourceCode":"\t\t\t\t\tReleaseEncryptedBoxOperation(boxID)\n\t\t\t\t}\n\t\t\t}()\n\t\t\t_, artifact, resolved = ResolveManagedEncryptedExport(fileName)\n\t\t\tif !resolved {\n\t\t\t\treturn nil, errors.New(\"managed export is unavailable\")\n\t\t\t}\n\t\t\tif _, dekErr := GetDEKIfUnlocked(boxID); dekErr != nil {\n\t\t\t\treturn nil, dekErr\n\t\t\t}\n\t\t\tlease, err = registerMobileExportLease(boxID, artifact, filepath.Base(fileName), \"\")\n\t\t\tif err == nil {\n\t\t\t\trelease = false\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tartifact := filepath.Join(util.TempDir, \"export\", fileName)\n\t\tif !gulu.File.IsSubPath(filepath.Join(util.TempDir, \"export\"), artifact) {\n\t\t\treturn nil, errors.New(\"export path is outside export directory\")\n\t\t}\n\t\treturn registerMobileExportLease(\"\", artifact, filepath.Base(fileName), \"\")\n\t}\n\n\tif !strings.HasPrefix(exportPath, \"assets/\") {\n\t\treturn nil, errors.New(\"unsupported export path\")\n\t}\n\trelativePath, boxID, parseErr := assetPathAndBox(exportPath, \"\")\n\tif parseErr != nil {\n\t\treturn nil, parseErr\n\t}\n\tif boxID == \"\" || !IsEncryptedBox(boxID) {\n\t\tartifact, resolveErr := GetAssetAbsPath(relativePath)\n\t\tif resolveErr != nil {\n\t\t\treturn nil, resolveErr\n\t\t}\n\t\tif ensureErr := EnsureAssetLocal(artifact); ensureErr != nil {\n\t\t\treturn nil, ensureErr","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/encrypted_export.go#L201-L237","documentation":"For non-managed export names, the artifact path is built as `<TempDir>/export/<fileName>` and then verified with gulu.File.IsSubPath. This error means the resulting absolute path is not inside the export directory — a defensive containment check that also catches symlink/normalization surprises.","triggerScenarios":"AcquireExportArtifactLease called with a name that, after joining with the temp export dir and cleaning, escapes `util.TempDir/export` (e.g. crafted names with separators, or asset paths outside `assets/` handled by adjacent checks).","commonSituations":"Client-supplied names containing path segments that survive filepath.Clean; platform-specific separator tricks; exporting on a system where TempDir is symlinked elsewhere.","solutions":["Send only simple file names without directory components to the export lease API.","Prefer managed export paths produced by the export flow over hand-built relative paths.","Verify the client is not double-joining paths (e.g. already prefixed with 'export/').","If TempDir is customized/symlinked, confirm the export directory resolves inside the workspace temp tree."],"exampleFix":"// before\n{name: \"sub\\..\\..\\report.html\"} // escapes TempDir/export after join\n// after\n{name: \"report.html\"}","handlingStrategy":"validation","validationCode":"// Go: verify the joined artifact stays inside the export dir before calling the API\nartifact := filepath.Join(util.TempDir, \"export\", name)\nif !gulu.File.IsSubPath(filepath.Join(util.TempDir, \"export\"), artifact) {\n    return errors.New(\"name escapes export directory\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Send plain file names without any directory components.","Do not pre-join client paths with 'export/' before calling the API.","Prefer managed export paths from the export flow for encrypted notebooks."],"tags":["go","security","path-traversal","export"],"backgroundTag":"path-traversal-blocked","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}