{"record":{"id":"4e325ef89849be4f","repo":"flipped-aurora/gin-vue-admin","slug":"invalid-plugin-archive-name","errorCode":null,"errorMessage":"invalid plugin archive name","messagePattern":"invalid plugin archive name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/service/system/auto_code_plugin.go","lineNumber":183,"sourceCode":"\t}\n\tif err = os.MkdirAll(serverRoot, 0755); err != nil {\n\t\treturn -1, -1, err\n\t}\n\ttempDir, err := os.MkdirTemp(serverRoot, \"gva-plugin-\")\n\tif err != nil {\n\t\treturn -1, -1, err\n\t}\n\tdefer os.RemoveAll(tempDir)\n\n\tsrc, err := file.Open()\n\tif err != nil {\n\t\treturn -1, -1, err\n\t}\n\tdefer src.Close()\n\n\tarchiveName := filepath.Base(strings.TrimSpace(file.Filename))\n\tif archiveName == \".\" || archiveName == string(filepath.Separator) {\n\t\treturn -1, -1, errors.New(\"invalid plugin archive name\")\n\t}\n\tarchivePath, err := utils.JoinWithinRoot(tempDir, archiveName)\n\tif err != nil {\n\t\treturn -1, -1, err\n\t}\n\tout, err := os.Create(archivePath)\n\tif err != nil {\n\t\treturn -1, -1, err\n\t}\n\tif _, err = io.Copy(out, src); err != nil {\n\t\tout.Close()\n\t\treturn -1, -1, err\n\t}\n\tif err = out.Close(); err != nil {\n\t\treturn -1, -1, err\n\t}\n\n\tpaths, err := utils.Unzip(archivePath, tempDir)","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/auto_code_plugin.go#L165-L201","documentation":"installSubPlugin validates the uploaded file's basename before writing it under tempDir: if the trimmed filename base is '.' or a path separator, there is no usable archive name, so it returns 'invalid plugin archive name'. This blocks empty/degenerate multipart filenames from creating bad temp paths.","triggerScenarios":"Uploading a sub-plugin archive via the sub-plugin install API with file.Filename empty, whitespace-only, '.', '/', or only separators — often from a programmatic client omitting filename in multipart/form-data.","commonSituations":"Custom scripts or SDK calls building multipart bodies without setting the filename part; browser edge cases where a directory was 'uploaded'; proxies stripping Content-Disposition filenames.","solutions":["Re-send the multipart request ensuring the filename is a real archive name, e.g. curl -F 'file=@myplugin.zip'.","On the client, validate the selected file has a non-empty name ending in .zip before upload.","Server-side, reject earlier at the API layer with a clearer message when file.Filename is blank."],"exampleFix":"// before\nbody.append('file', blob)            // no filename -> invalid\n// after\nbody.append('file', blob, 'myplugin.zip')\n// or\nformData.append('file', fileInput.files[0], fileInput.files[0].name)","handlingStrategy":"validation","validationCode":"const name = file.name?.trim()\nif (!name || name === '.' || name === '/' || name === '\\\\') {\n  throw new Error('invalid plugin archive name')\n}","typeGuard":"function hasValidArchiveName(file) {\n  const n = (file?.name ?? '').trim()\n  return n.length > 0 && n !== '.' && n !== '/' && n !== '\\\\'\n}","tryCatchPattern":"web, server, err := pluginService.Install(file)\nif err != nil && err.Error() == \"invalid plugin archive name\" {\n    return fmt.Errorf(\"上传文件缺少有效文件名，请在 multipart 中指定 filename: %w\", err)\n}","preventionTips":["When building multipart bodies programmatically, always pass an explicit filename.","Validate file input has a name ending in .zip before upload.","Check proxies/gateways aren't stripping Content-Disposition filename parameters."],"tags":["plugin","upload","filename-validation"],"backgroundTag":"invalid-filename","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}