Skip to content

Commit

Permalink
#19 Add extra test for directories
Browse files Browse the repository at this point in the history
    Check for trailing "/" when External Attributes has directory flag(s) set.
    Check if directory bit(s) set in External Attributes when training "/" is present.
    Check that directory entry does not have uncompressed payload (APPNOTE 6.3.10, sec 4.3.8)
    Check if compressed payload present & uncompresses to zero bytes.
    Check that Extract Version is set to 2.0 or greater (APPNOTE 6.3.10, sec 4.4.3.2)
  • Loading branch information
pmqs committed Apr 17, 2024
1 parent 24b2675 commit ef60982
Show file tree
Hide file tree
Showing 27 changed files with 891 additions and 15 deletions.
112 changes: 108 additions & 4 deletions bin/zipdetails
Original file line number Diff line number Diff line change
Expand Up @@ -2384,6 +2384,94 @@ sub redactFilename
return $filename;
}

sub validateDirectory
{
# Check that Directries are stored correctly
#
# 1. Filename MUST end with a "/"
# see APPNOTE 6.3.10, sec 4.3.8
# 2. Uncompressed size == 0
# see APPNOTE 6.3.10, sec 4.3.8
# 3. warn if compressed size > 0 and Uncompressed size == 0
# 4. check for presence of DOS directory attrib in External Attributes
# 5. Check for Unix extrnal attribute S_IFDIR

my $offset = shift ;
my $filename = shift ;
my $extractVersion = shift;
my $versionMadeBy = shift;
my $compressedSize = shift;
my $uncompressedSize = shift;
my $externalAttributes = shift;

my $dosAttributes = $externalAttributes & 0xFFFF;
my $otherAttributes = ($externalAttributes >> 16 ) & 0xFFFF;

my $probablyDirectory = 0;
my $filenameOK = 0;
my $attributesSet = 0;
my $dosAttributeSet = 0;
my $unixAttributeSet = 0;

if ($filename =~ m#/$#)
{
# filename claims it is a directory.
$probablyDirectory = 1;
$filenameOK = 1;
}

if ($dosAttributes & 0x0010) # ATTR_DIRECTORY
{
$probablyDirectory = 1;
$attributesSet = 1 ;
$dosAttributeSet = 1 ;
}

if ($versionMadeBy == 3 && $otherAttributes & 0x4000) # Unix & S_IFDIR
{
$probablyDirectory = 1;
$attributesSet = 1;
$unixAttributeSet = 1;
}

return
unless $probablyDirectory ;

error $offset + CentralDirectoryEntry::Offset_Filename(),
"Directory '$filename' must end in a '/'",
"'External Attributes' flag this as a directory"
if ! $filenameOK && $uncompressedSize == 0;

info $offset + CentralDirectoryEntry::Offset_ExternalAttributes(),
"DOS Directory flag not set in 'External Attributes' for Directory '$filename'"
if $filenameOK && ! $dosAttributeSet;

info $offset + CentralDirectoryEntry::Offset_ExternalAttributes(),
"Unix Directory flag not set in 'External Attributes' for Directory '$filename'"
if $filenameOK && $versionMadeBy == 3 && ! $unixAttributeSet;

if ($uncompressedSize != 0)
{
# APPNOTE 6.3.10, sec 4.3.8
error $offset + CentralDirectoryEntry::Offset_UncompressedSize(),
"Directory '$filename' must not have a payload"
}
elsif ($compressedSize != 0)
{

info $offset + CentralDirectoryEntry::Offset_CompressedSize(),
"Directory '$filename' has compressed payload that uncompresses to nothing"
}

if ($extractVersion < 20)
{
# APPNOTE 6.3.10, sec 4.4.3.2
my $got = decodeZipVer($extractVersion);
warning $offset + CentralDirectoryEntry::Offset_VersionNeededToExtract(),
"'Extract Zip Spec' is '$got'. Need value >= '2.0' for Directory '$filename'"
}
}

sub validateFilename
{
my $filename = shift ;
Expand Down Expand Up @@ -2734,10 +2822,6 @@ sub CentralHeader
$cdEntry->filename($raw_filename) ;
$filename = outputFilename($raw_filename, $LanguageEncodingFlag);
$cdEntry->outputFilename($filename);

# APPNOTE 6.3.10, sec 4.3.8
warning $FH->tell - $filenameLength, "Directory '$filename' must not have a payload"
if $filename =~ m#/$# && $uncompressedSize ;
}

$cdEntry->centralHeaderOffset($cdEntryOffset) ;
Expand Down Expand Up @@ -2767,6 +2851,8 @@ sub CentralHeader

# $cdEntry->endCentralHeaderOffset($FH->tell() - 1);

# Can only validate for directory after zip64 data is read
validateDirectory($cdEntryOffset, $filename, $extractVer, $made_by, $compressedSize, $uncompressedSize, $ext_file_attrib);

if ($comment_length)
{
Expand Down Expand Up @@ -6321,6 +6407,24 @@ sub nibbles

use parent -norequire , 'LocalCentralEntryBase' ;

use constant Offset_VersionMadeBy => 4;
use constant Offset_VersionNeededToExtract => 6;
use constant Offset_GeneralPurposeFlags => 8;
use constant Offset_CompressionMethod => 10;
use constant Offset_ModificationTime => 12;
use constant Offset_ModificationDate => 14;
use constant Offset_CRC32 => 16;
use constant Offset_CompressedSize => 20;
use constant Offset_UncompressedSize => 24;
use constant Offset_FilenameLength => 28;
use constant Offset_ExtraFieldLength => 30;
use constant Offset_FileCommentLength => 32;
use constant Offset_DiskNumber => 34;
use constant Offset_InternalAttributes => 36;
use constant Offset_ExternalAttributes => 38;
use constant Offset_RelativeOffsetToLocal => 42;
use constant Offset_Filename => 46;

sub new
{
my $class = shift ;
Expand Down
2 changes: 1 addition & 1 deletion t/002-main.t
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ my $tests_per_zip = 6 ;
my $tests_per_zip_full = $tests_per_zip * 2 * 3 * 2 ;

my $test_count = 1 ;
$test_count += 222 if -e 't/files';
$test_count += 225 if -e 't/files';

plan tests => $test_count * $tests_per_zip_full ;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@
0062 Local Header Offset 00000000 (0)
0066 Filename 'abc/'
#
# WARNING: Offset 0x66: Directory 'abc/' must not have a payload
# INFO: Offset 0x5E: DOS Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# INFO: Offset 0x5E: Unix Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# ERROR: Offset 0x50: Directory 'abc/' must not have a payload
#

006A END CENTRAL HEADER 06054B50 (101010256)
Expand All @@ -60,6 +64,8 @@
007A Offset to Central Dir 00000038 (56)
007E Comment Length 0000 (0)
#
# Warning Count: 2
# Error Count: 1
# Warning Count: 1
# Info Count: 2
#
# Done
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@
0062 0065 0004 00 00 00 00 Local Header Offset 00000000 (0)
0066 0069 0004 61 62 63 2F Filename 'abc/'
#
# WARNING: Offset 0x66: Directory 'abc/' must not have a payload
# INFO: Offset 0x5E: DOS Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# INFO: Offset 0x5E: Unix Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# ERROR: Offset 0x50: Directory 'abc/' must not have a payload
#

006A 006D 0004 50 4B 05 06 END CENTRAL HEADER 06054B50 (101010256)
Expand All @@ -61,6 +65,8 @@
007A 007D 0004 38 00 00 00 Offset to Central Dir 00000038 (56)
007E 007F 0002 00 00 Comment Length 0000 (0)
#
# Warning Count: 2
# Error Count: 1
# Warning Count: 1
# Info Count: 2
#
# Done
10 changes: 8 additions & 2 deletions t/files/0000-errors/badly-formed/directory-with-payload/stdout
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@
0052 Local Header Offset 00000000 (0)
0056 Filename 'abc/'
#
# WARNING: Offset 0x56: Directory 'abc/' must not have a payload
# INFO: Offset 0x4E: DOS Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# INFO: Offset 0x4E: Unix Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# ERROR: Offset 0x40: Directory 'abc/' must not have a payload
#

005A END CENTRAL HEADER 06054B50 (101010256)
Expand All @@ -53,6 +57,8 @@
006A Offset to Central Dir 00000028 (40)
006E Comment Length 0000 (0)
#
# Warning Count: 2
# Error Count: 1
# Warning Count: 1
# Info Count: 2
#
# Done
10 changes: 8 additions & 2 deletions t/files/0000-errors/badly-formed/directory-with-payload/stdout-v
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@
0052 0055 0004 00 00 00 00 Local Header Offset 00000000 (0)
0056 0059 0004 61 62 63 2F Filename 'abc/'
#
# WARNING: Offset 0x56: Directory 'abc/' must not have a payload
# INFO: Offset 0x4E: DOS Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# INFO: Offset 0x4E: Unix Directory flag not set in 'External Attributes' for Directory 'abc/'
#
# ERROR: Offset 0x40: Directory 'abc/' must not have a payload
#

005A 005D 0004 50 4B 05 06 END CENTRAL HEADER 06054B50 (101010256)
Expand All @@ -54,6 +58,8 @@
006A 006D 0004 28 00 00 00 Offset to Central Dir 00000028 (40)
006E 006F 0002 00 00 Comment Length 0000 (0)
#
# Warning Count: 2
# Error Count: 1
# Warning Count: 1
# Info Count: 2
#
# Done
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

0000 LOCAL HEADER #1 04034B50 (67324752)
0004 Extract Zip Spec 0A (10) '1.0'
0005 Extract OS 00 (0) 'MS-DOS'
0006 General Purpose Flag 0000 (0)
0008 Compression Method 0000 (0) 'Stored'
000A Modification Time 579C9419 (1469879321) 'Thu Dec 28 18:32:50 2023'
000E CRC 00000000 (0)
0012 Compressed Size 00000000 (0)
0016 Uncompressed Size 00000000 (0)
001A Filename Length 0008 (8)
001C Extra Length 001C (28)
001E Filename 'somedire'
0026 Extra ID #1 5455 (21589) 'Extended Timestamp [UT]'
0028 Length 0009 (9)
002A Flags 03 (3) 'Modification Access'
002B Modification Time 658DBF51 (1703788369) 'Thu Dec 28 18:32:49 2023'
002F Access Time 658DBF51 (1703788369) 'Thu Dec 28 18:32:49 2023'
0033 Extra ID #2 7875 (30837) 'Unix Extra type 3 [ux]'
0035 Length 000B (11)
0037 Version 01 (1)
0038 UID Size 04 (4)
0039 UID 000003E8 (1000)
003D GID Size 04 (4)
003E GID 000003E8 (1000)

0042 CENTRAL HEADER #1 02014B50 (33639248)
0046 Created Zip Spec 1E (30) '3.0'
0047 Created OS 03 (3) 'Unix'
0048 Extract Zip Spec 0A (10) '1.0'
0049 Extract OS 00 (0) 'MS-DOS'
004A General Purpose Flag 0000 (0)
004C Compression Method 0000 (0) 'Stored'
004E Modification Time 579C9419 (1469879321) 'Thu Dec 28 18:32:50 2023'
0052 CRC 00000000 (0)
0056 Compressed Size 00000000 (0)
005A Uncompressed Size 00000000 (0)
005E Filename Length 0008 (8)
0060 Extra Length 0018 (24)
0062 Comment Length 0000 (0)
0064 Disk Start 0000 (0)
0066 Int File Attributes 0000 (0)
[Bit 0] 0 'Binary Data'
0068 Ext File Attributes 41FD0010 (1107099664)
[Bit 4] Directory
[Bits 16-24] 01FD (509) 'Unix attrib: rwxrwxr-x'
[Bits 28-31] 04 (4) 'Directory'
006C Local Header Offset 00000000 (0)
0070 Filename 'somedire'
0078 Extra ID #1 5455 (21589) 'Extended Timestamp [UT]'
007A Length 0005 (5)
007C Flags 03 (3) 'Modification Access'
007D Modification Time 658DBF51 (1703788369) 'Thu Dec 28 18:32:49 2023'
0081 Extra ID #2 7875 (30837) 'Unix Extra type 3 [ux]'
0083 Length 000B (11)
0085 Version 01 (1)
0086 UID Size 04 (4)
0087 UID 000003E8 (1000)
008B GID Size 04 (4)
008C GID 000003E8 (1000)
#
# ERROR: Offset 0x70: Directory 'somedire' must end in a '/'
# 'External Attributes' flag this as a directory
#
# WARNING: Offset 0x48: 'Extract Zip Spec' is '1.0'. Need value >= '2.0' for Directory 'somedire'
#

0090 END CENTRAL HEADER 06054B50 (101010256)
0094 Number of this disk 0000 (0)
0096 Central Dir Disk no 0000 (0)
0098 Entries in this disk 0001 (1)
009A Total Entries 0001 (1)
009C Size of Central Dir 0000004E (78)
00A0 Offset to Central Dir 00000042 (66)
00A4 Comment Length 0000 (0)
#
# Error Count: 1
# Warning Count: 1
#
# Done

0 comments on commit ef60982

Please sign in to comment.