{"record":{"id":"b1dbc37c25dafcdf","repo":"flipped-aurora/gin-vue-admin","slug":"key-b1dbc3","errorCode":null,"errorMessage":"非法的key","messagePattern":"非法的key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/utils/upload/local.go","lineNumber":115,"sourceCode":"\n\terr = os.Remove(p)\n\tif err != nil {\n\t\treturn errors.New(\"文件删除失败: \" + err.Error())\n\t}\n\n\treturn nil\n}\n\n// localPath 校验 key 并拼接出本地存储的绝对路径，复用 DeleteFile 中的路径穿越防护逻辑。\nfunc (*Local) localPath(ctx context.Context, key string) (string, error) {\n\t// 检查 key 是否为空\n\tif key == \"\" {\n\t\treturn \"\", errors.New(\"key不能为空\")\n\t}\n\n\t// 验证 key 是否包含非法字符或尝试访问存储路径之外的文件\n\tif strings.Contains(key, \"..\") || strings.ContainsAny(key, `\\/:*?\"<>|`) {\n\t\treturn \"\", errors.New(\"非法的key\")\n\t}\n\n\treturn filepath.Join(global.GVA_CONFIG.Local.StorePath, key), nil\n}\n\n// Exists 检查本地文件是否存在，\"不存在\"统一降级为 (false, nil)。\nfunc (l *Local) Exists(ctx context.Context, key string) (bool, error) {\n\tp, err := l.localPath(ctx, key)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tinfo, err := os.Stat(p)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil\n\t\t}\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(err).Error(\"function os.Stat() failed\")","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/upload/local.go#L97-L133","documentation":"This validation error is returned by the internal localPath helper when the key contains path-traversal sequences ('..') or characters illegal in file names (\\ / : * ? \" < > |). It is a security guard preventing callers from addressing files outside the configured local store path.","triggerScenarios":"Calling DeleteFile or Exists with a key such as \"../secret.txt\", \"a/b.png\", or any key containing backslashes or Windows-reserved characters — usually keys produced by another storage backend (e.g. MinIO/S3 keys with slashes) being passed to the local adapter.","commonSituations":"Mixing storage backends: S3/MinIO object keys use '/' separators and are rejected by the local adapter; user-supplied filenames reaching the delete path unsanitized; attacker-crafted keys probing for path traversal.","solutions":["Pass the bare filename key exactly as returned by UploadFile (flat, slash-free filename), not a path or a foreign backend's object key.","Sanitize/normalize keys at ingestion: strip directories and reject illegal characters before persisting them.","If you must support hierarchical keys, extend localPath to safely join and re-verify the result stays within StorePath (filepath.Clean + prefix check) instead of blanket rejection.","Never construct keys from raw user input; derive them from the stored record's filename field produced by UploadFile."],"exampleFix":"// before\nkey := objectURL // \"https://cdn/x/a.png\" or \"sub/dir/a.png\" from MinIO\nlocal.DeleteFile(ctx, key) // 非法的key\n// after\nkey := filepath.Base(strings.ReplaceAll(objectURL, \"\\\\\", \"/\")) // \"a.png\"\nlocal.DeleteFile(ctx, key)","handlingStrategy":"validation","validationCode":"func isSafeLocalKey(key string) bool {\n    if strings.TrimSpace(key) == \"\" {\n        return false\n    }\n    if strings.Contains(key, \"..\") || strings.ContainsAny(key, `\\/:*?\"<>|`) {\n        return false\n    }\n    return true\n}\n// usage: if !isSafeLocalKey(key) { return errors.New(\"invalid key\") }","typeGuard":"type SafeKey string\nfunc newSafeKey(raw string) (SafeKey, bool) {\n    if strings.Contains(raw, \"..\") || strings.ContainsAny(raw, `\\/:*?\"<>|`) {\n        return \"\", false\n    }\n    return SafeKey(raw), true\n}","tryCatchPattern":"err := local.DeleteFile(ctx, key)\nif err != nil && err.Error() == \"非法的key\" {\n    return fmt.Errorf(\"key %q is not a local storage key (slashes/illegal chars)\", key)\n}","preventionTips":["Store the flat filename returned by UploadFile as the canonical key and delete by that.","Never pass foreign backend keys (S3/MinIO paths) directly to the local adapter.","Sanitize user-supplied filenames with filepath.Base at ingestion time.","Treat this error as a security signal — log the rejected key and investigate its origin."],"tags":["security","path-traversal","validation","go"],"backgroundTag":"path-traversal-attempt","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}