diff --git a/bin/evening_detective_server b/bin/evening_detective_server index f6b86f3..cdb0d8b 100755 Binary files a/bin/evening_detective_server and b/bin/evening_detective_server differ diff --git a/internal/services/file_service/service.go b/internal/services/file_service/service.go index 5765c3e..587e7a4 100644 --- a/internal/services/file_service/service.go +++ b/internal/services/file_service/service.go @@ -96,6 +96,9 @@ func (s *FileService) UploadFile( // Категория по содержимому (сырой sniff): text/* и octet-stream не // проходят вообще, расширение должно соответствовать категории. sniffed := http.DetectContentType(data) + if ext == ".mp3" && isMP3(data) { + sniffed = "audio/mpeg" + } fileType := contentTypeCategory(sniffed) if fileType == "" || fileType != file_storage.FileType(filename) { return "", "", ErrInvalidContentType @@ -117,6 +120,13 @@ func (s *FileService) UploadFile( return storedName, fileType, nil } +// isMP3 распознаёт MP3 без необязательного ID3-тега по заголовку MPEG-фрейма. +func isMP3(data []byte) bool { + return len(data) >= 4 && data[0] == 0xff && data[1]&0xe0 == 0xe0 && + data[1]&0x18 != 0x08 && data[1]&0x06 != 0 && + data[2]&0xf0 != 0 && data[2]&0xf0 != 0xf0 && data[2]&0x0c != 0x0c +} + // contentTypeCategory возвращает категорию по sniffed-типу: // application/pdf → pdf; image/* → image; audio/* и application/ogg → audio; // всё остальное (text/*, application/octet-stream и пр.) → "". diff --git a/internal/services/file_service/service_test.go b/internal/services/file_service/service_test.go index 258d4e5..cfae99c 100644 --- a/internal/services/file_service/service_test.go +++ b/internal/services/file_service/service_test.go @@ -71,6 +71,7 @@ func TestUploadFileValid(t *testing.T) { {"photo.PNG", []byte("\x89PNG\r\n\x1a\ncontent"), "image", ".png", "image/png"}, {"photo.jpg", []byte("\xff\xd8\xff\xe0jpeg-content"), "image", ".jpg", "image/jpeg"}, {"audio.mp3", []byte("ID3\x04\x00\x00\x00\x00\x00\x00audio"), "audio", ".mp3", "audio/mpeg"}, + {"audio-no-id3.mp3", []byte("\xff\xfb\x90\x64audio"), "audio", ".mp3", "audio/mpeg"}, {"audio.ogg", []byte("OggS\x00\x02audio"), "audio", ".ogg", "application/ogg"}, {"audio.wav", []byte("RIFF\x24\x00\x00\x00WAVEfmt "), "audio", ".wav", "audio/wave"}, } @@ -126,6 +127,7 @@ func TestUploadFileValidation(t *testing.T) { {"base из не-латиницы (!!!.pdf)", "!!!.pdf", pdf, ErrEmptyName}, {"html под видом png", "photo.png", []byte("hi"), ErrInvalidContentType}, {"octet-stream", "clue.pdf", []byte{0x00, 0x01, 0x02, 0x03}, ErrInvalidContentType}, + {"ложный mp3 sync", "audio.mp3", []byte{0xff, 0xe0, 0x00, 0x00}, ErrInvalidContentType}, {"mismatch pdf+png", "clue.pdf", []byte("\x89PNG\r\n\x1a\ncontent"), ErrInvalidContentType}, } for _, c := range cases {