{"record":{"id":"d3634b56cc4dc9ae","repo":"larksuite/cli","slug":"s-path-must-be-absolute-got-q","errorCode":null,"errorMessage":"%s: path must be absolute, got %q","messagePattern":"(.+?): path must be absolute, got %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/binding/audit.go","lineNumber":75,"sourceCode":"\n\tif err := auditFilePermissions(effectivePath, params.AllowReadableByOthers, label); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := checkOwnerUID(effectivePath, label); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn effectivePath, nil\n}\n\n// requireAbsolutePath rejects relative paths; relative paths would depend on\n// the process cwd and defeat the point of a static audit. Shell-style\n// shortcuts like `~` are home-relative, not cwd-relative — they are an\n// orthogonal concern and the audit is intentionally Go-stdlib strict here.\n// Callers that accept user-authored config (e.g. resolveFileRef) must\n// pre-resolve any such shortcuts before passing the path in.\nfunc requireAbsolutePath(target, label string) error {\n\tif !filepath.IsAbs(target) {\n\t\treturn fmt.Errorf(\"%s: path must be absolute, got %q\", label, target)\n\t}\n\treturn nil\n}\n\n// lstatNonDir stats the path without following symlinks, rejecting\n// directories. Returns the stat info for downstream steps to reuse.\nfunc lstatNonDir(target, label string) (fs.FileInfo, error) {\n\tinfo, err := vfs.Lstat(target)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: cannot stat %q: %w\", label, target, err)\n\t}\n\tif info.IsDir() {\n\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","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/binding/audit.go#L57-L93","documentation":"requireAbsolutePath (invoked via AssertSecurePath in the binding security audit) rejects any path that is not absolute under Go's filepath.IsAbs rules. The audit is intentionally stdlib-strict: shortcuts like '~' are home-relative and must be pre-resolved by callers such as resolveFileRef before audit. The label names which audited path failed.","triggerScenarios":"AssertSecurePath is given a relative path (e.g. 'config.yaml', './x', '../x') or a '~'-prefixed path that the caller failed to expand before auditing.","commonSituations":"User config file contains a relative path that was never resolved against cwd/home; a '~' shortcut passed straight from user-authored config into the audit; programmatic callers building paths by concatenation without filepath.Abs.","solutions":["Convert to an absolute path at the call site: filepath.Abs(p) or home-dir expansion for '~' before calling AssertSecurePath.","For '~', expand to os.UserHomeDir()-based absolute path in the config-loading layer (resolveFileRef), not in the audit.","Fix the config value itself to be an absolute path if it's user-authored.","Check the label in the message to identify which of the audited paths (file, target, symlink) is relative."],"exampleFix":"// before\nerr := binding.AssertSecurePath(cfg.Path, params)\n// after\nabs, _ := filepath.Abs(expandTilde(cfg.Path))\nerr := binding.AssertSecurePath(abs, params)","handlingStrategy":"validation","validationCode":"func ensureAbs(p string) (string, error) {\n    if strings.HasPrefix(p, \"~\") {\n        home, err := os.UserHomeDir()\n        if err != nil { return \"\", err }\n        p = filepath.Join(home, strings.TrimPrefix(p, \"~\"))\n    }\n    return filepath.Abs(p)\n}\nabs, err := ensureAbs(cfg.Path)\nif err != nil { return err }\nerr = binding.AssertSecurePath(abs, params)","typeGuard":"func isRelativePathErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"path must be absolute\")\n}","tryCatchPattern":"if err := binding.AssertSecurePath(p, params); err != nil {\n    if isRelativePathErr(err) {\n        abs, aerr := filepath.Abs(p)\n        if aerr == nil { return binding.AssertSecurePath(abs, params) }\n    }\n    return err\n}","preventionTips":["Always resolve '~' and relative paths in the config-loading layer before audit.","Run filepath.Abs at the boundary where user input enters the program.","Store absolute paths in config files when the consumer requires them.","Read the label in the error to identify which audited path is relative."],"tags":["path-validation","security","filesystem","go"],"backgroundTag":"path-not-absolute","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"}