{"record":{"id":"55f376f4c9565a09","repo":"siyuan-note/siyuan","slug":"staged-document-tree-contains-a-symbolic-link","errorCode":null,"errorMessage":"staged document tree contains a symbolic link","messagePattern":"staged document tree contains a symbolic link","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":2415,"sourceCode":"\nfunc copyObsidianTreeFiles(sourceRoot, destinationRoot string) error {\n\treturn filepath.WalkDir(sourceRoot, func(current string, entry fs.DirEntry, walkErr error) error {\n\t\tif walkErr != nil {\n\t\t\treturn walkErr\n\t\t}\n\t\trel, err := filepath.Rel(sourceRoot, current)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif rel == \".\" {\n\t\t\treturn nil\n\t\t}\n\t\tdestination := filepath.Join(destinationRoot, rel)\n\t\tif entry.IsDir() {\n\t\t\treturn os.MkdirAll(destination, 0755)\n\t\t}\n\t\tif entry.Type()&os.ModeSymlink != 0 {\n\t\t\treturn errors.New(\"staged document tree contains a symbolic link\")\n\t\t}\n\t\treturn filelock.Copy(current, destination)\n\t})\n}\n\nfunc availableObsidianNotebookName(requested string) string {\n\tbase := sanitizeObsidianTitle(requested)\n\texisting := map[string]bool{}\n\tboxes, _ := ListNotebooks()\n\tfor _, box := range boxes {\n\t\texisting[strings.ToLower(box.Name)] = true\n\t}\n\tif !existing[strings.ToLower(base)] {\n\t\treturn base\n\t}\n\tfor index := 2; ; index++ {\n\t\tcandidate := fmt.Sprintf(\"%s (%d)\", base, index)\n\t\tif !existing[strings.ToLower(candidate)] {","sourceCodeStart":2397,"sourceCodeEnd":2433,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/import_obsidian.go#L2397-L2433","documentation":"While copying the staged temporary document tree into the target notebook, the copier walks each entry and explicitly rejects symbolic links. Symlinks in the staged tree could escape the destination root or break on export, so the copy aborts with this error.","triggerScenarios":"filepath.WalkDir over the staged docs temp directory encounters a file entry whose Type() has os.ModeSymlink set — i.e. someone or something placed a symlink into the staging directory before/during the copy to destinationRoot.","commonSituations":"A malicious or buggy step (or third-party tool) created symlinks in the temp staging directory; a user pre-seeded the temp directory with symlinked assets; importing on a filesystem that materializes links (some cloud mounts); a previous interrupted run left links behind.","solutions":["Inspect the staging directory for symlinks (find -type l) and remove them, then retry the import","Re-run the import with a clean temporary directory so stale links are not copied","Ensure no tool between staging and copy replaces files with symlinks (sync/backup/link farms)","If symlinks are intentional, replace them with real file copies before import"],"exampleFix":"// before: symlink present in staging dir gets rejected\n// staged/assets/logo.png -> /etc/passwd (symlink)\ncopyTree(stagedDir, destinationRoot) // errors\n// after: dereference real copies before staging\nfilepath.Walk(vaultAssets, func(p string, info os.FileInfo, err error) error {\n\tif info.Mode()&os.ModeSymlink != 0 {\n\t\ttarget, _ := os.Readlink(p)\n\t\treturn os.Remove(p) // or copy target contents over the link\n\t}\n\treturn nil\n})","handlingStrategy":"validation","validationCode":"err := filepath.WalkDir(stagedDir, func(p string, d fs.DirEntry, err error) error {\n\tif err == nil && d.Type()&os.ModeSymlink != 0 {\n\t\treturn fmt.Errorf(\"symlink in staging: %s\", p)\n\t}\n\treturn err\n})","typeGuard":"func isSymlink(d fs.DirEntry) bool { return d.Type()&os.ModeSymlink != 0 }","tryCatchPattern":"if err := copyStagedTree(src, dst); err != nil && strings.Contains(err.Error(), \"symbolic link\") {\n\t// clean staging dir, remove links, retry\n}","preventionTips":["Always start from a fresh staging directory per import run","Never place symlinks into the staging tree; copy real files instead","Scan for -type l entries before copying"],"tags":["symlink","security","file-copy","staging"],"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-14T00:17:10.932Z"}