File Coverage

blib/lib/Mail/Exim/ACL/Attachments.pm
Criterion Covered Total %
statement 47 47 100.0
branch 12 14 85.7
condition 1 2 50.0
subroutine 8 8 100.0
pod 2 2 100.0
total 70 73 95.8


line stmt bran cond sub pod time code
1             package Mail::Exim::ACL::Attachments;
2              
3             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
4              
5 1     1   199359 use 5.016;
  1         3  
6 1     1   4 use warnings;
  1         1  
  1         37  
7 1     1   14 use utf8;
  1         1  
  1         10  
8              
9             our $VERSION = 1.006;
10              
11 1     1   43 use Exporter qw(import);
  1         1  
  1         31  
12 1     1   663 use IO::Uncompress::Unzip;
  1         22448  
  1         1582  
13              
14             our @EXPORT_OK = qw(check_filename check_zip);
15              
16             # https://docs.microsoft.com/en-us/deployoffice/compat/office-file-format-reference
17             # https://en.wikipedia.org/wiki/List_of_Microsoft_Office_filename_extensions
18             our %MACRO_ENABLED = (
19             doc => 'Word Document',
20             docb => 'Word Document',
21             docm => 'Word Document',
22             dot => 'Word Template',
23             dotm => 'Word Template',
24             one => 'OneNote Export File',
25             pot => 'PowerPoint Template',
26             potm => 'PowerPoint Template',
27             ppa => 'PowerPoint Add-in',
28             ppam => 'PowerPoint Add-in',
29             pps => 'PowerPoint Slide Show',
30             ppsm => 'PowerPoint Slide Show',
31             ppt => 'PowerPoint Presentation',
32             pptm => 'PowerPoint Presentation',
33             sldm => 'PowerPoint Slide',
34             vsd => 'Visio Drawing File',
35             vsdm => 'Visio Drawing File',
36             vss => 'Visio Stencil File',
37             vssm => 'Visio Stencil File',
38             vst => 'Visio Drawing Template',
39             vstm => 'Visio Drawing Template',
40             xla => 'Excel Add-in',
41             xlam => 'Excel Add-in',
42             xlm => 'Excel Macro',
43             xls => 'Excel Spreadsheet',
44             xlsb => 'Excel Spreadsheet',
45             xlsm => 'Excel Spreadsheet',
46             xlt => 'Excel Spreadsheet Template',
47             xltm => 'Excel Spreadsheet Template',
48             xlw => 'Excel Workspace',
49             );
50              
51             # https://support.office.com/en-us/article/blocked-attachments-in-outlook-434752e1-02d3-4e90-9124-8b81e49a8519
52             our %BLOCKED_BY_OUTLOOK = (
53             ade => 'Access Project Extension',
54             adp => 'Access Project',
55             app => 'Executable Application',
56             application => 'ClickOnce Deployment Manifest File',
57             'appref-ms' => 'ClickOnce Application Reference File',
58             asp => 'Active Server Page',
59             aspx => 'Active Server Page Extended',
60             asx => 'ASF Redirector file',
61             bas => 'BASIC Source Code',
62             bat => 'Batch Processing',
63             bgi => 'Borland Graphics Interface',
64             cab => 'Windows Cabinet File',
65             cer => 'Internet Security Certificate File',
66             chm => 'Compiled HTML Help',
67             cmd => 'Command File',
68             cnt => 'Microsoft Help Workshop Application',
69             com => 'Command',
70             cpl => 'Windows Control Panel Extension',
71             crt => 'Certificate File',
72             csh => 'csh Script',
73             der => 'DER Encoded X509 Certificate File',
74             diagcab => 'Microsoft Support diagnostic tools',
75             exe => 'Executable File',
76             fxp => 'FoxPro Compiled Source',
77             gadget => 'Windows Vista gadget',
78             grp => 'Microsoft program group',
79             hlp => 'Windows Help File',
80             hpj => 'AppWizard Help project',
81             hta => 'Hypertext Application',
82             htc => 'HTML component file',
83             inf => 'Information or Setup File',
84             ins => 'IIS Internet Communications Settings',
85             iso => 'Optical Disk Media File System',
86             isp => 'IIS Internet Service Provider Settings',
87             its => 'Internet Document Set',
88             jar => 'Java Archive',
89             jnlp => 'Java Network Launch Protocol',
90             js => 'JavaScript Source Code',
91             jse => 'JScript Encoded Script File',
92             ksh => 'UNIX Shell Script',
93             lnk => 'Windows Shortcut File',
94             mad => 'Access Module Shortcut',
95             maf => 'Access',
96             mag => 'Access Diagram Shortcut',
97             mam => 'Access Macro Shortcut',
98             maq => 'Access Query Shortcut',
99             mar => 'Access Report Shortcut',
100             mas => 'Access Stored Procedures',
101             mat => 'Access Table Shortcut',
102             mau => 'Media Attachment Unit',
103             mav => 'Access View Shortcut',
104             maw => 'Access Data Access Page',
105             mcf => 'Media Container Format',
106             mda => 'Access Add-in',
107             mdb => 'Access Application',
108             mde => 'Access MDE Database File',
109             mdt => 'Access Add-in Data',
110             mdw => 'Access Workgroup Information',
111             mdz => 'Access Wizard Template',
112             msc => 'Microsoft Management Console Snap-in Control File',
113             msh => 'Microsoft Shell',
114             msh1 => 'Microsoft Shell',
115             msh2 => 'Microsoft Shell',
116             mshxml => 'Microsoft Shell',
117             msh1xml => 'Microsoft Shell',
118             msh2xml => 'Microsoft Shell',
119             msi => 'Windows Installer File',
120             msp => 'Windows Installer Update',
121             mst => 'Windows SDK Setup Transform Script',
122             msu => 'Windows Update file',
123             ops => 'Office Profile Settings File',
124             osd => 'Open Software Description ',
125             pcd => 'Visual Test',
126             pif => 'Windows Program Information File',
127             pl => 'Perl script',
128             plg => 'Developer Studio Build Log',
129             prf => 'Windows System File',
130             prg => 'Program File',
131             printerexport => 'Printer backup file',
132             ps1 => 'Windows PowerShell',
133             ps1xml => 'Windows PowerShell',
134             ps2 => 'Windows PowerShell',
135             ps2xml => 'Windows PowerShell',
136             psc1 => 'Windows PowerShell',
137             psc2 => 'Windows PowerShell',
138             psd1 => 'Windows PowerShell',
139             psdm1 => 'Windows PowerShell',
140             pst => 'Outlook Personal Folder File',
141             py => 'Python script',
142             pyc => 'Python script',
143             pyo => 'Python script',
144             pyw => 'Python script',
145             pyz => 'Python script',
146             pyzw => 'Python script',
147             reg => 'Registry Data File',
148             scf => 'Windows Explorer Command',
149             scr => 'Windows Screen Saver',
150             sct => 'Windows Script Component',
151             shb => 'Windows Shortcut into a Document',
152             shs => 'Shell Scrap Object File',
153             theme => 'Desktop theme file settings',
154             tmp => 'Temporary File/Folder',
155             url => 'Internet Location',
156             vb => 'VBScript File or Any Visual Basic Source',
157             vbe => 'VBScript Encoded Script File',
158             vbp => 'Visual Basic project file',
159             vbs => 'VBScript File',
160             vhd => 'Virtual Hard Disk',
161             vhdx => 'Virtual Hard Disk Extended',
162             vsmacros => 'Visual Studio .NET Binary-based Macro Project',
163             vsw => 'Visio Workspace File',
164             webpnp => 'Internet printing file',
165             website => 'Pinned site shortcut from Internet Explorer',
166             ws => 'Windows Script File',
167             wsc => 'Windows Script Component',
168             wsf => 'Windows Script File',
169             wsh => 'Windows Script Host Settings File',
170             xbap => 'Browser applications',
171             xll => 'Excel Add-in',
172             xnk => 'Exchange Public Folder Shortcut',
173             );
174              
175             # File associations from 7-Zip and others
176             our %ARCHIVES = (
177             '7z' => '7-Zip Archive',
178             ace => 'ACE File',
179             arj => 'ARJ File',
180             bz2 => 'bzip2 File',
181             bzip2 => 'bzip2 File',
182             cpio => 'CPIO Archive',
183             deb => 'Debian Package',
184             dll => 'Dynamic Link Library',
185             dmg => 'Disk Image',
186             esd => 'Disk Image',
187             fat => 'Zip Archive',
188             gz => 'gzip File',
189             gzip => 'gzip File',
190             hfs => 'Disk Image',
191             lha => 'LHA File',
192             lz => 'Lzip Compressed File',
193             lzh => 'LZH File',
194             lzma => 'LZMA File',
195             ntfs => 'Disk Image',
196             rar => 'RAR Archive',
197             rpm => 'RPM File',
198             sfx => 'Self-extracting Archive',
199             squashfs => 'Disk Image',
200             swm => 'Disk Image',
201             tar => 'tar Archive',
202             taz => 'tar Archive',
203             tbz => 'tar Archive',
204             tbz2 => 'tar Archive',
205             tgz => 'tar Archive',
206             tpz => 'tar Archive',
207             txz => 'tar Archive',
208             uue => 'uuencoded File',
209             wim => 'Disk Image',
210             xar => 'XAR File',
211             xz => 'XZ File',
212             z => 'gzip File',
213             zip => 'Zip Archive',
214             );
215              
216             our %BLOCKLIST = (%MACRO_ENABLED, %BLOCKED_BY_OUTLOOK, %ARCHIVES);
217              
218             sub check_filename {
219 9     9 1 288786 my $filename = shift;
220              
221 9         16 my $extension = q{};
222 9 50       70 if ($filename =~ m{[.]\h*([^.]+)\z}) {
223 9         31 $extension = lc $1;
224             }
225              
226 9 100       31 if (exists $BLOCKLIST{$extension}) {
227 5         22 return 'blocked';
228             }
229              
230             # Reject split archives like "001" and "r01".
231 4 100       17 if ($extension =~ m{\A[r\d]\d{2,}\z}) {
232 2         12 return 'blocked';
233             }
234              
235 2         11 return 'ok';
236             }
237              
238             sub _get_filename {
239 4     4   10 my $zip = shift;
240              
241 4         5 my $filename;
242              
243 4         5 my $header = eval { $zip->getHeaderInfo };
  4         13  
244 4 100       18 if (defined $header) {
245 3         6 $filename = $header->{Name};
246             }
247              
248 4         10 return $filename;
249             }
250              
251             sub check_zip {
252 4     4 1 2015 my $input = shift;
253              
254 4         5 my $result = 'blocked';
255              
256 4         5 my $zip = eval { IO::Uncompress::Unzip->new($input) };
  4         28  
257 4 50       5544 if (defined $zip) {
258 4         20 my $status = 1;
259             STREAM:
260 4         10 while ($status > 0) {
261 4         9 my $filename = _get_filename($zip);
262 4 100       9 if (defined $filename) {
263 3         5 $result = check_filename($filename);
264             }
265             else {
266 1         2 $result = 'blocked';
267             }
268 4 100       8 if ($result ne 'ok') {
269 3         6 last STREAM;
270             }
271 1   50     1 $status = eval { $zip->nextStream } // -1;
  1         9  
272             }
273 4         151 $zip->close;
274             }
275              
276 4         55 return $result;
277             }
278              
279             1;
280             __END__