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