{"record":{"id":"e20100d9e6957116","repo":"Tencent/WeKnora","slug":"invalid-file-name-w-e20100","errorCode":null,"errorMessage":"invalid file name: %w","messagePattern":"invalid file name: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/application/service/file/s3.go","lineNumber":328,"sourceCode":"\t\tBucket:     aws.String(s.bucketName),\n\t\tCopySource: aws.String(s.bucketName + \"/\" + srcKey),\n\t\tKey:        aws.String(destKey),\n\t})\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to copy file in S3: %w\", err)\n\t}\n\n\tnewPath := fmt.Sprintf(\"s3://%s/%s\", s.bucketName, destKey)\n\tlogger.Infof(ctx, \"Copied S3 object %s to %s\", srcPath, newPath)\n\treturn newPath, nil\n}\n\n// SaveBytes saves bytes data to S3 and returns the file path\n// temp parameter is ignored for S3 (no auto-expiration support in this implementation)\nfunc (s *s3FileService) SaveBytes(ctx context.Context, data []byte, tenantID uint64, fileName string, temp bool) (string, error) {\n\tsafeName, err := utils.SafeFileName(fileName)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid file name: %w\", err)\n\t}\n\text := filepath.Ext(safeName)\n\tobjectName := fmt.Sprintf(\"%s%d/exports/%s%s\", s.pathPrefix, tenantID, uuid.New().String(), ext)\n\n\t// Upload bytes to S3\n\treader := bytes.NewReader(data)\n\t_, err = s.client.PutObject(ctx, &s3.PutObjectInput{\n\t\tBucket:        aws.String(s.bucketName),\n\t\tKey:           aws.String(objectName),\n\t\tBody:          reader,\n\t\tContentLength: aws.Int64(int64(len(data))),\n\t\tContentType:   aws.String(utils.GetContentTypeByExt(ext)),\n\t})\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to upload bytes to S3: %w\", err)\n\t}\n\n\treturn fmt.Sprintf(\"s3://%s/%s\", s.bucketName, objectName), nil","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/s3.go#L310-L346","documentation":"SaveBytes validates the incoming fileName with utils.SafeFileName before uploading; this error wraps a validation failure. The provided name was empty, contained illegal path characters, or could not be sanitized into a safe S3 key component, so nothing was uploaded.","triggerScenarios":"Calling SaveBytes with an empty fileName, a name containing path separators ('/', '../'), null bytes, or other characters rejected by SafeFileName.","commonSituations":"User-supplied upload filenames passed straight through from an HTTP multipart form; filenames from other filesystems (Windows backslashes); empty name when the client did not send a filename.","solutions":["Validate/sanitize fileName at the API boundary before calling SaveBytes (strip path components, reject empty)","Provide a server-generated default name (e.g. uuid or timestamp) when the client filename is empty","Unwrap the SafeFileName error to see which character/rule failed and normalize the input accordingly","Reject unsafe names with a 400 response instead of a 500 from the storage layer"],"exampleFix":"// before\npath, err := svc.SaveBytes(ctx, data, tenantID, r.Header.Get(\"X-Filename\"), false)\n// after\nname := filepath.Base(r.Header.Get(\"X-Filename\"))\nif name == \"\" || name == \".\" {\n    name = uuid.New().String()\n}\npath, err := svc.SaveBytes(ctx, data, tenantID, name, false)","handlingStrategy":"validation","validationCode":"if fileName == \"\" { return errors.New(\"file name required\") }\nsafe, err := utils.SafeFileName(fileName)\nif err != nil { return fmt.Errorf(\"rejecting upload: %w\", err) }","typeGuard":"func isSafeFileName(name string) bool {\n    return name != \"\" && !strings.ContainsAny(name, \"/\\\\\\x00\") && utils.SafeFileName(name) == nil == false || utils.SafeFileName(name) == nil\n}","tryCatchPattern":"path, err := svc.SaveBytes(ctx, data, tenantID, fileName, false)\nif err != nil && strings.Contains(err.Error(), \"invalid file name\") {\n    return httputil.BadRequest(\"invalid file name\")\n}","preventionTips":["Sanitize user-supplied filenames at the HTTP boundary before storage","Default to a generated name when the client sends none","Strip directory components with filepath.Base","Return 400 (client error) not 500 for invalid names"],"tags":["validation","filename","s3","input"],"backgroundTag":"invalid-filename","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}