{"record":{"id":"2c1178cc3d1daab5","repo":"wavetermdev/waveterm","slug":"invalid-source-path-w","errorCode":null,"errorMessage":"invalid source path: %w","messagePattern":"invalid source path: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/waveappstore/waveappstore.go","lineNumber":390,"sourceCode":"\t\treturn nil, err\n\t}\n\n\treturn fileutil.ReplaceInFilePartial(filePath, edits)\n}\n\nfunc RenameAppFile(appId string, fromFileName string, toFileName string) error {\n\tif err := ValidateAppId(appId); err != nil {\n\t\treturn fmt.Errorf(\"invalid appId: %w\", err)\n\t}\n\n\tappDir, err := GetAppDir(appId)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfromPath, err := validateAndResolveFilePath(appDir, fromFileName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid source path: %w\", err)\n\t}\n\n\ttoPath, err := validateAndResolveFilePath(appDir, toFileName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid destination path: %w\", err)\n\t}\n\n\tif err := os.MkdirAll(filepath.Dir(toPath), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create destination directory: %w\", err)\n\t}\n\n\tif err := os.Rename(fromPath, toPath); err != nil {\n\t\treturn fmt.Errorf(\"failed to rename file: %w\", err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/waveappstore/waveappstore.go#L372-L408","documentation":"After appId validation, RenameAppFile resolves the SOURCE file name via validateAndResolveFilePath and wraps any failure as 'invalid source path' (waveappstore.go:388-391). The resolver rejects absolute paths, '..' path traversal, and any path that escapes the app directory — this prevents renaming files outside ~/waveapps/<ns>/<app>.","triggerScenarios":"Calling RenameAppFile / RenameAppFileCommand with fromFileName that is absolute ('/etc/passwd'), contains '..' segments ('../shared.txt'), cleans to escape the app dir, or otherwise fails validateAndResolveFilePath.","commonSituations":"Frontends forwarding user-typed paths that include leading '/' or '../'; code joining a base dir into fileName before calling (producing an absolute path); attempts to move files between apps by using '../otherapp/file' instead of two rename operations.","solutions":["Pass fromFileName as a plain relative path inside the app, e.g. 'old.txt' or 'sub/dir/file.go'.","Strip any leading '/' and resolve '..' segments before calling; reject them if the result escapes the app.","If the goal is cross-app or cross-namespace moves, do ReadAppFile + WriteAppFile + DeleteAppFile instead of '..' traversal.","Check the wrapped sub-error: it names the exact rule violated (absolute path / path traversal / escapes app directory)."],"exampleFix":"// before\nwaveappstore.RenameAppFile(appId, \"/home/user/waveapps/local/app/old.txt\", \"new.txt\") // absolute rejected\n\n// after\nerr := waveappstore.RenameAppFile(appId, \"old.txt\", \"new.txt\") // relative to app dir\nif err != nil && strings.Contains(err.Error(), \"invalid source path\") {\n    return fmt.Errorf(\"source must be a relative path inside the app: %w\", err)\n}","handlingStrategy":"validation","validationCode":"func safeRelName(name string) (string, error) {\n    if filepath.IsAbs(name) {\n        return \"\", fmt.Errorf(\"must be relative: %s\", name)\n    }\n    clean := filepath.Clean(name)\n    if strings.HasPrefix(clean, \"..\") || strings.Contains(clean, string(filepath.Separator)+\"..\") {\n        return \"\", fmt.Errorf(\"traversal not allowed: %s\", name)\n    }\n    return clean, nil\n}\n// run on fromFileName (and toFileName) before RenameAppFile","typeGuard":null,"tryCatchPattern":"err := waveappstore.RenameAppFile(appId, fromName, toName)\nif err != nil {\n    var kind string\n    switch {\n    case strings.Contains(err.Error(), \"invalid source path\"):\n        kind = \"source\"\n    case strings.Contains(err.Error(), \"invalid destination path\"):\n        kind = \"destination\"\n    }\n    return fmt.Errorf(\"rename rejected (%s): %w\", kind, err)\n}","preventionTips":["Always pass plain relative paths like 'dir/file.txt' — never absolute paths or '..' segments.","Sanitize user-typed paths in the UI before calling rename.","For cross-app moves, use ReadAppFile + WriteAppFile + DeleteAppFile instead of traversal.","Clean and re-check names with filepath.Clean plus a '..' prefix check."],"tags":["validation","go","path-traversal","security","rename"],"backgroundTag":"path-traversal-rejected","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}