WTEL-9538: Fix MIME-type mismatch#183
Conversation
| func resolveUrlUploadMime(res *http.Response, clientHint string) (io.ReadCloser, string, model.AppError) { | ||
| parsed, _, parseErr := mime.ParseMediaType(res.Header.Get("Content-Type")) | ||
| if parseErr == nil && strings.Contains(parsed, "/") && parsed != "application/octet-stream" { | ||
| return res.Body, parsed, nil | ||
| } | ||
|
|
||
| head := make([]byte, 512) | ||
| n, readErr := io.ReadFull(res.Body, head) | ||
| if readErr != nil && readErr != io.EOF && readErr != io.ErrUnexpectedEOF { | ||
| return nil, "", model.NewInternalError("grpc.upload_file_url.read_head", readErr.Error()) | ||
| } | ||
| head = head[:n] | ||
| body := io.NopCloser(io.MultiReader(bytes.NewReader(head), res.Body)) | ||
|
|
||
| kind, _ := filetype.Match(head) | ||
| switch { | ||
| case kind != filetype.Unknown: | ||
| detected := kind.MIME.Value | ||
| if clientHint != "" && clientHint != detected { | ||
| return nil, "", model.PolicyErrorExtSuspicious | ||
| } | ||
| return body, detected, nil | ||
| case clientHint != "": | ||
| return body, clientHint, nil | ||
| default: | ||
| return body, "", nil | ||
| } | ||
| } |
There was a problem hiding this comment.
загалом, схожа перевірка вже є нижче по флоу, в PolicyReader.testMimeType (виконується
коли файл уже стрімиться у сторадж):
Line 286 in f7e601f
там матч робиться через strings.HasPrefix(declared, detected), тож, наприклад,
declared=image/jpeg; charset=binary + detected=image/jpeg пройде. тут порівняння через точну рівність, тобто той самий кейс впаде з PolicyErrorExtSuspicious.
думаю, варто вирівняти логіку і нормалізувати clientHint через
https://pkg.go.dev/mime#ParseMediaType (зріже параметри + lowercase)
| case clientHint != "": | ||
| return body, clientHint, nil | ||
| default: | ||
| return body, "", nil |
There was a problem hiding this comment.
а що робити потім з порожнім mime-type?
може тут помилку?
There was a problem hiding this comment.
потім воно також фейлиться, але краще було одразу зробити fail fast, переробив
Проблема
storage.FileService/UploadFileUrlповертав 403policy.file.allowпри завантаженні файлів за URL з S3, який повертає некоректний заголовокContent-Type: image(без subtype). Існуючий fallback на клієнтський mime спрацьовував лише для точногоapplication/octet-stream, тому валіднийimage/jpegвід chat-server ігнорувався, а сам "image" не знаходив відповідну політику через строге співставлення вMatchPattern.Вирішення
Додано нормалізацію та валідацію MIME-типу перед передачею у політику. Якщо заголовок
Content-Typeвалідний (парситься черезmime.ParseMediaTypeі неapplication/octet-stream) то використовуємо його. Інакше зчитуємо перші 512 байт тіла і визначаємо тип черезfiletype.Match(перевірка за магічними байтами). КлієнтськийMimeтепер використовується лише як останній резерв, коли sniffing не дав результату. Якщо детектований тип не збігається з клієнтським hint то відмова черезPolicyErrorExtSuspicious(захист від підміни).