]> BookStack Code Mirror - bookstack/blob - app/Util/WebSafeMimeSniffer.php
Vectors: Added command to regenerate for all
[bookstack] / app / Util / WebSafeMimeSniffer.php
1 <?php
2
3 namespace BookStack\Util;
4
5 use finfo;
6
7 /**
8  * Helper class to sniff out the mime-type of content resulting in
9  * a mime-type that's relatively safe to serve to a browser.
10  */
11 class WebSafeMimeSniffer
12 {
13     /**
14      * @var string[]
15      */
16     protected array $safeMimes = [
17         'application/json',
18         'application/octet-stream',
19         'application/pdf',
20         'audio/aac',
21         'audio/midi',
22         'audio/mpeg',
23         'audio/ogg',
24         'audio/opus',
25         'audio/wav',
26         'audio/webm',
27         'audio/x-m4a',
28         'image/apng',
29         'image/bmp',
30         'image/jpeg',
31         'image/png',
32         'image/gif',
33         'image/webp',
34         'image/avif',
35         'image/heic',
36         'text/css',
37         'text/csv',
38         'text/javascript',
39         'text/json',
40         'text/plain',
41         'video/x-msvideo',
42         'video/mp4',
43         'video/mpeg',
44         'video/ogg',
45         'video/webm',
46         'video/vp9',
47         'video/h264',
48         'video/av1',
49     ];
50
51     protected array $textTypesByExtension = [
52         'css' => 'text/css',
53         'js' => 'text/javascript',
54         'json' => 'application/json',
55         'csv' => 'text/csv',
56     ];
57
58     /**
59      * Sniff the mime-type from the given file content while running the result
60      * through an allow-list to ensure a web-safe result.
61      * Takes the content as a reference since the value may be quite large.
62      * Accepts an optional $extension which can be used for further guessing.
63      */
64     public function sniff(string &$content, string $extension = ''): string
65     {
66         $fInfo = new finfo(FILEINFO_MIME_TYPE);
67         $mime = $fInfo->buffer($content) ?: 'application/octet-stream';
68
69         if ($mime === 'text/plain' && $extension) {
70             $mime = $this->textTypesByExtension[$extension] ?? 'text/plain';
71         }
72
73         if (in_array($mime, $this->safeMimes)) {
74             return $mime;
75         }
76
77         [$category] = explode('/', $mime, 2);
78         if ($category === 'text') {
79             return 'text/plain';
80         }
81
82         return 'application/octet-stream';
83     }
84 }