hyperledger/fabric · error
refusing to copy symlink %s -> %s pointing outside of source
Error message
refusing to copy symlink %s -> %s pointing outside of source root
What it means
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.
Source
Thrown at core/container/externalbuilder/copy.go:78
// If the symlink is absolute, then we do not want to copy it.
symlinkDest, err := os.Readlink(srcpath)
if err != nil {
return err
}
if filepath.IsAbs(symlinkDest) {
return errors.Errorf("refusing to copy absolute symlink %s -> %s", srcpath, symlinkDest)
}
// Determine where the symlink points to. If it points outside
// of the source root, then we do not want to copy it.
symlinkDir := filepath.Dir(srcpath)
symlinkTarget := filepath.Clean(filepath.Join(symlinkDir, symlinkDest))
relativeTarget, err := filepath.Rel(srcroot, symlinkTarget)
if err != nil {
return err
}
if relativeTargetElements := strings.Split(relativeTarget, string(os.PathSeparator)); len(relativeTargetElements) >= 1 && relativeTargetElements[0] == ".." {
return errors.Errorf("refusing to copy symlink %s -> %s pointing outside of source root", srcpath, symlinkDest)
}
return os.Symlink(symlinkDest, destpath)
}
func copyFile(srcpath, destpath string) error {
srcFile, err := os.Open(srcpath)
if err != nil {
return err
}
defer srcFile.Close()
info, err := srcFile.Stat()
if err != nil {
return err
}
destFile, err := os.Create(destpath)View on GitHub (pinned to 2736b63f8f)
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
Example fix
// before ln -s ../../../shared/libfoo.so lib/libfoo.so // after cp ../../../shared/libfoo.so lib/libfoo.so
Defensive patterns
Strategy: validation
Validate before calling
target, _ := filepath.EvalSymlinks(linkPath)
root, _ := filepath.EvalSymlinks(srcRoot)
rel, err := filepath.Rel(root, target)
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return fmt.Errorf("symlink escapes package: %s", linkPath)
} Type guard
func symlinkInsideRoot(link, root string) bool {
target, err := filepath.EvalSymlinks(link)
if err != nil { return false }
absRoot, _ := filepath.Abs(root)
return strings.HasPrefix(target, absRoot+string(os.PathSeparator))
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- refusing to copy absolute symlink %s -> %s
- tar contains the absolute or escaping path '%s'
- peer will not accept external chaincode connection %s (excep
- illegal file detected in payload: "%s"
- chaincode %s attempted to write to the namespace of LSCC
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/147f97737b03a834.
Report an issue: GitHub.