{"record":{"id":"f72c94da236fe067","repo":"larksuite/cli","slug":"s-cannot-resolve-symlink-q-w","errorCode":null,"errorMessage":"%s: cannot resolve symlink %q: %w","messagePattern":"(.+?): cannot resolve symlink %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/binding/audit.go","lineNumber":106,"sourceCode":"\t\treturn nil, fmt.Errorf(\"%s: path %q is a directory, not a file\", label, target)\n\t}\n\treturn info, nil\n}\n\n// resolveSymlinkIfAllowed resolves a symlink to its target when\n// params.AllowSymlinkPath is true, or rejects it otherwise. When the input\n// is not a symlink, target is returned unchanged. A symlink that points to\n// another symlink is rejected so callers only deal with a single hop.\nfunc resolveSymlinkIfAllowed(target string, linfo fs.FileInfo, params AuditParams) (string, error) {\n\tif linfo.Mode()&os.ModeSymlink == 0 {\n\t\treturn target, nil\n\t}\n\tif !params.AllowSymlinkPath {\n\t\treturn \"\", fmt.Errorf(\"%s: path %q is a symlink (not allowed)\", params.Label, target)\n\t}\n\tresolved, err := vfs.EvalSymlinks(target)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%s: cannot resolve symlink %q: %w\", params.Label, target, err)\n\t}\n\trinfo, err := vfs.Lstat(resolved)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%s: cannot stat resolved path %q: %w\", params.Label, resolved, err)\n\t}\n\tif rinfo.Mode()&os.ModeSymlink != 0 {\n\t\treturn \"\", fmt.Errorf(\"%s: resolved path %q is still a symlink\", params.Label, resolved)\n\t}\n\treturn resolved, nil\n}\n\n// requireInTrustedDirs enforces that effectivePath lives under one of the\n// caller-declared trusted directories, if any were declared. An empty\n// trustedDirs list disables the check.\nfunc requireInTrustedDirs(effectivePath string, trustedDirs []string, label string) error {\n\tif len(trustedDirs) == 0 {\n\t\treturn nil\n\t}","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/binding/audit.go#L88-L124","documentation":"AssertSecurePath audits a file path used for secret/command binding. When the target is a symlink and AllowSymlinkPath is true, resolveSymlinkIfAllowed calls vfs.EvalSymlinks to fully resolve it; this error wraps any failure from that resolution (e.g. a dangling link, a permission-denied directory along the chain, or an ELOOP cycle). It is thrown to prevent auditing an unresolvable path.","triggerScenarios":"Target path is a symlink, params.AllowSymlinkPath is true, and EvalSymlinks fails: broken symlink (target does not exist), too many levels of symlink indirection, or a directory component of the link chain denies search permission to the current user.","commonSituations":"Config points at /usr/local/bin/foo which is a symlink whose target was uninstalled; symlinks pointing into a root-only directory while the CLI runs unprivileged; home-dir symlinks into unmounted volumes (e.g. dead autofs/NFS mounts); symlink loops after manual relinking.","solutions":["Check the wrapped cause with errors.Unwrap / %w output: if it is 'no such file or directory', recreate the symlink so it points at an existing file","If the path should be a plain file, replace the symlink with the real file or unset the symlink-related config entry","Verify every directory component in the symlink chain is readable/executable by the current user (ls -ld each component)","Ensure the storage backing the symlink target is mounted (NFS/autofs/home volume)","Keep symlink chain length to a single hop — the audit rejects multi-hop links anyway"],"exampleFix":"// before: dangling link\nln -s /opt/old-tool/bin/agent /usr/local/bin/agent\n// after: point at an existing target\nln -s /opt/tool/bin/agent /usr/local/bin/agent","handlingStrategy":"validation","validationCode":"func precheckSymlink(p string) error {\n  fi, err := os.Lstat(p)\n  if err != nil { return err }\n  if fi.Mode()&os.ModeSymlink == 0 { return nil }\n  resolved, err := filepath.EvalSymlinks(p)\n  if err != nil { return fmt.Errorf(\"symlink %s unresolvable: %w\", p, err) }\n  if _, err := os.Stat(resolved); err != nil { return err }\n  return nil\n}","typeGuard":"func isSymlink(fi fs.FileInfo) bool { return fi.Mode()&os.ModeSymlink != 0 }","tryCatchPattern":null,"preventionTips":["Run readlink -f on configured paths before saving them into config","Never leave dangling symlinks in directories referenced by config","Avoid symlink chains; point directly at the final file","Keep symlink target directories accessible to the user running the CLI"],"tags":["filesystem","symlink","security-audit","path-resolution"],"backgroundTag":"symlink-resolution-failed","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}