{"record":{"id":"4219ff0562b1397f","repo":"larksuite/cli","slug":"local-input-path-must-not-contain-control-characte","errorCode":null,"errorMessage":"local input path must not contain control characters","messagePattern":"local input path must not contain control characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/vfs/localfileio/path.go","lineNumber":65,"sourceCode":"// flags via stdin (\"-\").\nfunc SafeInputPath(path string) (string, error) {\n\treturn safePath(path, \"--file\")\n}\n\n// LocalInputPath validates an input path in the process local filesystem\n// namespace. It intentionally does not impose allowlist containment or\n// canonicalize the returned path: absolute paths, parent-relative paths, and\n// symlink traversal retain their normal OS semantics (the grandfathered\n// apps-upload exception, see #2005). The built-in denylist still applies:\n// even the relaxed tier may not reach protected directories. Character\n// validation remains mandatory because paths are user-controlled and may\n// appear in errors or progress output.\nfunc LocalInputPath(path string) (string, error) {\n\tif strings.TrimSpace(path) == \"\" {\n\t\treturn \"\", fmt.Errorf(\"local input path must not be empty\")\n\t}\n\tif strings.IndexFunc(path, unicode.IsControl) >= 0 {\n\t\treturn \"\", fmt.Errorf(\"local input path must not contain control characters\")\n\t}\n\tif err := charcheck.RejectControlChars(path, \"local input path\"); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := validateLocalInputPlatform(path); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := denyCheckLocalInput(path); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn path, nil\n}\n\n// denyCheckLocalInput applies the built-in denylist to the relaxed local\n// input tier. Resolution is fail-closed like safePath, but the allowlist is\n// deliberately not consulted here. This tier hands the path back verbatim, so\n// every interpretation of it is checked — the caller opens the one the OS\n// picks, which for \"~/...\" is a literal \"~\" entry in the working directory.","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/vfs/localfileio/path.go#L47-L83","documentation":"LocalInputPath rejects any input path containing control characters (unicode.IsControl, plus charcheck.RejectControlChars for the dangerous set). Paths are user-controlled and get embedded in error messages and progress output, where control characters enable log injection or terminal manipulation.","triggerScenarios":"Calling localfileio.LocalInputPath with a path containing \\n, \\r, \\t, \\x1b, \\x00, or other control code points — typically from unparsed multi-line input, split errors, or tampered configuration.","commonSituations":"A file list read with line endings left in each entry (CRLF not trimmed); data pasted from a PDF/chat into a script; a config value containing escape sequences; malicious input attempting log injection.","solutions":["Trim/strip control characters from the path before passing it, e.g. strings.TrimFunc(p, unicode.IsControl) for leading/trailing \\r\\n","Fix the producer: read lines with a scanner that strips line terminators instead of raw splitting","Quote/sanitize any external input before placing it in a path; reject values containing \\x00 or escape chars","If the filename legitimately contains a control character, rename the file"],"exampleFix":"// before\np, err := localfileio.LocalInputPath(line) // line came from strings.Split(data, \"\\n\")\n// after\nline = strings.TrimSpace(line)\np, err := localfileio.LocalInputPath(line)","handlingStrategy":"validation","validationCode":"if strings.IndexFunc(p, unicode.IsControl) >= 0 {\n    return fmt.Errorf(\"path %q contains control characters\", p)\n}","typeGuard":"func safePathChars(p string) bool {\n    return strings.IndexFunc(p, unicode.IsControl) < 0 && !strings.ContainsRune(p, 0)\n}","tryCatchPattern":"p, err := localfileio.LocalInputPath(flagValue)\nif err != nil {\n    return fmt.Errorf(\"--file: %w\", err)\n}","preventionTips":["Trim line terminators when reading paths from files or lists","Sanitize external/pasted input before using it in paths","Reject or escape values containing \\x00 and escape sequences early"],"tags":["validation","path","control-characters","input-sanitization"],"backgroundTag":"control-characters-in-path","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"}