{"record":{"id":"3d725b83d67690cb","repo":"flipped-aurora/gin-vue-admin","slug":"error-3d725b","errorCode":null,"errorMessage":"文件名不合法","messagePattern":"文件名不合法","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_skills.go","lineNumber":590,"sourceCode":"\t\t}\n\t\treturn \"\", \"\", fmt.Errorf(\"%s已存在\", label)\n\t}\n\tif err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tcontent := defaultContent\n\tif err := os.WriteFile(filePath, []byte(content), 0644); err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\treturn cleanName, content, nil\n}\n\nfunc (s *SkillsService) readSkillFile(tool, skill, subDir, fileName string) (string, error) {\n\tif !isSafeName(skill) {\n\t\treturn \"\", errors.New(\"技能名称不合法\")\n\t}\n\tif !isSafeFileName(fileName) {\n\t\treturn \"\", errors.New(\"文件名不合法\")\n\t}\n\tskillDir, err := s.skillDir(tool, skill)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tfilePath := filepath.Join(skillDir, subDir, fileName)\n\tcontent, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(content), nil\n}\n\nfunc (s *SkillsService) writeSkillFile(tool, skill, subDir, fileName, content string) error {\n\tif !isSafeName(skill) {\n\t\treturn errors.New(\"技能名称不合法\")\n\t}\n\tif !isSafeFileName(fileName) {","sourceCodeStart":572,"sourceCodeEnd":608,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_skills.go#L572-L608","documentation":"readSkillFile validates the skill name and file name before joining them into a path under the skill directory. It returns 文件名不合法 when isSafeFileName(fileName) fails, i.e. the file name is blank after trimming, contains '..', contains '/' or '\\\\', or is not equal to its own filepath.Base. This guards against path traversal when reading skill scripts/resources/references/templates.","triggerScenarios":"Calling GetScript, GetResource, GetReference or GetTemplate with a fileName that is empty/whitespace, includes a subdirectory path (e.g. 'sub/file.py'), contains '..' (e.g. '../../etc/passwd'), or uses backslashes on Linux.","commonSituations":"Frontend passes a stored relative path including folders instead of the bare file name; user-supplied input contains path separators; attempts to read files outside the skill dir; empty fileName after a string split on '/'.","solutions":["Pass only the bare base file name (no directories, no '..') as fileName.","Trim and validate the file name client-side before calling the API; reject empty or path-containing values.","If a nested file is intended, the API must be extended — readSkillFile only supports files directly in subDir.","Check that the name was not URL-encoded with %2F (%2e%2e is also rejected by the '..' check)."],"exampleFix":"// before\nsvc.GetScript(tool, skill, \"scripts\", \"../other/tool.py\")\n// after\nsvc.GetScript(tool, skill, \"scripts\", \"tool.py\")","handlingStrategy":"validation","validationCode":"func safeFileName(s string) bool {\n\tt := strings.TrimSpace(s)\n\treturn t != \"\" && !strings.Contains(t, \"..\") &&\n\t\t!strings.ContainsAny(t, \"/\\\\\") && t == filepath.Base(t)\n}\nif !safeFileName(fileName) { return fmt.Errorf(\"invalid file name %q\", fileName) }","typeGuard":"func isBareFileName(s string) bool { return s != \"\" && s == filepath.Base(s) && !strings.ContainsAny(s, \"\\\\/\") && !strings.Contains(s, \"..\") }","tryCatchPattern":"name, err := svc.GetScript(tool, skill, subDir, fileName)\nif err != nil {\n\tif strings.Contains(err.Error(), \"文件名不合法\") {\n\t\t// sanitize: keep only filepath.Base, retry once\n\t\tfileName = filepath.Base(fileName)\n\t\tname, err = svc.GetScript(tool, skill, subDir, fileName)\n\t}\n\tif err != nil { return err }\n}","preventionTips":["Always pass filepath.Base(path) of any client-side path.","Validate names with the same rules the server uses (no '..', no separators).","Never build file names by string concatenation of user input and paths.","Log the raw input when this error occurs to spot encoding issues (%2F)."],"tags":["path-traversal","validation","file-io","go"],"backgroundTag":"invalid-file-name","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}