{"record":{"id":"147f97737b03a834","repo":"hyperledger/fabric","slug":"refusing-to-copy-symlink-s-s-pointing-outside","errorCode":null,"errorMessage":"refusing to copy symlink %s -> %s pointing outside of source root","messagePattern":"refusing to copy symlink (.+?) -> (.+?) pointing outside of source root","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/container/externalbuilder/copy.go","lineNumber":78,"sourceCode":"\t// If the symlink is absolute, then we do not want to copy it.\n\tsymlinkDest, err := os.Readlink(srcpath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif filepath.IsAbs(symlinkDest) {\n\t\treturn errors.Errorf(\"refusing to copy absolute symlink %s -> %s\", srcpath, symlinkDest)\n\t}\n\n\t// Determine where the symlink points to. If it points outside\n\t// of the source root, then we do not want to copy it.\n\tsymlinkDir := filepath.Dir(srcpath)\n\tsymlinkTarget := filepath.Clean(filepath.Join(symlinkDir, symlinkDest))\n\trelativeTarget, err := filepath.Rel(srcroot, symlinkTarget)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif relativeTargetElements := strings.Split(relativeTarget, string(os.PathSeparator)); len(relativeTargetElements) >= 1 && relativeTargetElements[0] == \"..\" {\n\t\treturn errors.Errorf(\"refusing to copy symlink %s -> %s pointing outside of source root\", srcpath, symlinkDest)\n\t}\n\n\treturn os.Symlink(symlinkDest, destpath)\n}\n\nfunc copyFile(srcpath, destpath string) error {\n\tsrcFile, err := os.Open(srcpath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer srcFile.Close()\n\n\tinfo, err := srcFile.Stat()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdestFile, err := os.Create(destpath)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/container/externalbuilder/copy.go#L60-L96","documentation":"During a copy of package contents, a symlink whose resolved target escapes the source root is rejected. Fabric refuses to follow symlinks that would read or write files outside the extracted package directory, preventing path traversal from malicious chaincode packages.","triggerScenarios":"An external builder package contains a symlink whose destination, when joined with its directory and resolved relative to the source root, begins with '..' — i.e. it points outside the extracted package (e.g. symlink -> '../../etc/passwd' or an absolute-target symlink).","commonSituations":"Developers packaging chaincode that symlinks shared libraries or config from elsewhere on disk; build scripts generating relative symlinks with too many '..' segments; tarballs created with absolute symlink targets.","solutions":["Remove or rewrite the symlink so its target resolves inside the source package directory","Copy the actual file content into the package instead of symlinking external files","Recreate the package tarball ensuring symlinks use relative paths within the package"],"exampleFix":"// before\nln -s ../../../shared/libfoo.so lib/libfoo.so\n// after\ncp ../../../shared/libfoo.so lib/libfoo.so","handlingStrategy":"validation","validationCode":"target, _ := filepath.EvalSymlinks(linkPath)\nroot, _ := filepath.EvalSymlinks(srcRoot)\nrel, err := filepath.Rel(root, target)\nif err != nil || rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(os.PathSeparator)) {\n    return fmt.Errorf(\"symlink escapes package: %s\", linkPath)\n}","typeGuard":"func symlinkInsideRoot(link, root string) bool {\n    target, err := filepath.EvalSymlinks(link)\n    if err != nil { return false }\n    absRoot, _ := filepath.Abs(root)\n    return strings.HasPrefix(target, absRoot+string(os.PathSeparator))\n}","tryCatchPattern":null,"preventionTips":["Package only relative symlinks that resolve inside the package","Copy real files instead of symlinking external resources","Audit package tarballs for absolute or escaping symlink targets before install"],"tags":["security","symlink","path-traversal","external-builder"],"backgroundTag":"symlink-path-traversal","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}