summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpmqs <pmqs@cpan.org>2024-04-27 09:23:23 +0100
committerpmqs <pmqs@cpan.org>2024-04-27 09:23:23 +0100
commitea1fcf80793c61079c6e9680286a3a2b185c944f (patch)
treeb442f7ed91d31478459934e30d924c09b66c8610
parent08bab59d0a60198abb4d6a3b7b3da2a99aeae8a1 (diff)
Update zipdetails to version 4.003
see https://github.com/pmqs/zipdetails/releases/tag/v4.003 for changes
-rwxr-xr-xbin/zipdetails191
1 files changed, 154 insertions, 37 deletions
diff --git a/bin/zipdetails b/bin/zipdetails
index f961253..7dd1f55 100755
--- a/bin/zipdetails
+++ b/bin/zipdetails
@@ -29,7 +29,7 @@ use Encode;
use Getopt::Long;
use List::Util qw(min max);
-my $VERSION = '4.002' ;
+my $VERSION = '4.004' ;
sub fatal_tryWalk;
sub fatal_truncated ;
@@ -1424,7 +1424,7 @@ exit $exit_status_code ;
sub dislayMessages
{
- # Compare Central & LOcal for discrepencies
+ # Compare Central & Local for discrepencies
if ($CentralDirectory->isMiniZipEncrypted)
{
@@ -2063,10 +2063,6 @@ sub LocalHeader
$localEntry->filename($raw_filename) ;
$filename = outputFilename($raw_filename, $LanguageEncodingFlag);
$localEntry->outputFilename($filename);
-
- # APPNOTE 6.3.10, sec 4.3.8
- warning $FH->tell - $filenameLength, "Directory '$filename' must not have a payload"
- if ! $streaming && $filename =~ m#/$# && $uncompressedSize ;
}
$localEntry->localHeaderOffset($locHeaderOffset) ;
@@ -2094,6 +2090,10 @@ sub LocalHeader
walkExtra($extraLength, $localEntry);
}
+ # APPNOTE 6.3.10, sec 4.3.8
+ warning $FH->tell - $filenameLength, "Directory '$filename' must not have a payload"
+ if ! $streaming && $filename =~ m#/$# && $localEntry->uncompressedSize ;
+
my @msg ;
# if ($cdZip64 && ! $ZIP64)
# {
@@ -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 ;
@@ -2624,30 +2712,28 @@ sub CentralHeader
# See https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
# and https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-smb/65e0c225-5925-44b0-8104-6b91339c709f
- out1 "[Bit 0]", "Read-Only"
- if $dos_attrib & 0x0001 ;
- out1 "[Bit 1]", "Hidden"
- if $dos_attrib & 0x0002 ;
- out1 "[Bit 2]", "System"
- if $dos_attrib & 0x0004 ;
- out1 "[Bit 3]", "Label"
- if $dos_attrib & 0x0008 ;
- out1 "[Bit 4]", "Directory"
- if $dos_attrib & 0x0010 ;
- out1 "[Bit 5]", "Archive"
- if $dos_attrib & 0x0020 ;
- out1 "[Bit 6]", "Device"
- if $dos_attrib & 0x0040 ;
- out1 "[Bit 7]", "Normal" # Exe?
- if $dos_attrib & 0x0080 ;
- out1 "[Bit 8]", "Offline"
- if $dos_attrib & 0x0100 ;
- out1 "[Bit 9]", "Not Indexed"
- if $dos_attrib & 0x0200 ;
- out1 "[Bit 10]", "Encrypted"
- if $dos_attrib & 0x0400 ;
- out1 "[Bits 11-15]", Value_v($dos_attrib & 0xf800) . " 'Unknown DOS attrib'"
- if $dos_attrib & 0xf800 ;
+ out1 "[Bit 0]", "Read-Only" if $dos_attrib & 0x0001 ;
+ out1 "[Bit 1]", "Hidden" if $dos_attrib & 0x0002 ;
+ out1 "[Bit 2]", "System" if $dos_attrib & 0x0004 ;
+ out1 "[Bit 3]", "Label" if $dos_attrib & 0x0008 ;
+ out1 "[Bit 4]", "Directory" if $dos_attrib & 0x0010 ;
+ out1 "[Bit 5]", "Archive" if $dos_attrib & 0x0020 ;
+ out1 "[Bit 6]", "Device" if $dos_attrib & 0x0040 ;
+ out1 "[Bit 7]", "Normal" if $dos_attrib & 0x0080 ;
+ out1 "[Bit 8]", "Temporary" if $dos_attrib & 0x0100 ;
+ out1 "[Bit 9]", "Sparse" if $dos_attrib & 0x0200 ;
+ out1 "[Bit 10]", "Reparse Point" if $dos_attrib & 0x0400 ;
+ out1 "[Bit 11]", "Compressed" if $dos_attrib & 0x0800 ;
+
+ out1 "[Bit 12]", "Offline" if $dos_attrib & 0x1000 ;
+ out1 "[Bit 13]", "Not Indexed" if $dos_attrib & 0x2000 ;
+
+ # Zip files created on Mac seem to set this bit. Not clear why.
+ out1 "[Bit 14]", "Possible Mac Flag" if $dos_attrib & 0x4000 ;
+
+ # p7Zip & 7z set this bit to flag that the high 16-bits are Unix attributes
+ out1 "[Bit 15]", "Possible p7zip/7z Unix Flag" if $dos_attrib & 0x8000 ;
+
}
my $native_attrib = ($ext_file_attrib >> 16 ) & 0xFFFF;
@@ -2724,7 +2810,7 @@ sub CentralHeader
if ($locHeaderOffset != MAX32)
{
my $commonMessage = "'Local Header Offset' field in '" . Signatures::name($signature) . "' is invalid";
- $locHeaderOffset = checkOffsetValue($locHeaderOffset, $startRecordOffset, 0, $commonMessage, $startRecordOffset + 42, ZIP_LOCAL_HDR_SIG) ;
+ $locHeaderOffset = checkOffsetValue($locHeaderOffset, $startRecordOffset, 0, $commonMessage, $startRecordOffset + CentralDirectoryEntry::Offset_RelativeOffsetToLocal(), ZIP_LOCAL_HDR_SIG) ;
}
my $filename = '';
@@ -2736,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) ;
@@ -2769,6 +2851,9 @@ sub CentralHeader
# $cdEntry->endCentralHeaderOffset($FH->tell() - 1);
+ # Can only validate for directory after zip64 data is read
+ validateDirectory($cdEntryOffset, $filename, $extractVer, $made_by,
+ $cdEntry->compressedSize, $cdEntry->uncompressedSize, $ext_file_attrib);
if ($comment_length)
{
@@ -5655,7 +5740,9 @@ sub scanCentralDirectory
# 3. value at offset is not a CD record signature
my $commonMessage = "'Local Header Offset' field in '" . Signatures::name(ZIP_CENTRAL_HDR_SIG) . "' is invalid";
- checkOffsetValue($locHeaderOffset, $startHeader, 0, $commonMessage, $startHeader + 42, ZIP_LOCAL_HDR_SIG, 1) ;
+ checkOffsetValue($locHeaderOffset, $startHeader, 0, $commonMessage,
+ $startHeader + CentralDirectoryEntry::Offset_RelativeOffsetToLocal(),
+ ZIP_LOCAL_HDR_SIG, 1) ;
}
$fh->read(my $filename, $filename_length) ;
@@ -6323,6 +6410,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 ;
@@ -6551,6 +6656,18 @@ sub nibbles
use parent -norequire , 'LocalCentralEntryBase' ;
+ use constant Offset_VersionNeededToExtract => 4;
+ use constant Offset_GeneralPurposeFlags => 6;
+ use constant Offset_CompressionMethod => 8;
+ use constant Offset_ModificationTime => 10;
+ use constant Offset_ModificationDate => 12;
+ use constant Offset_CRC32 => 14;
+ use constant Offset_CompressedSize => 18;
+ use constant Offset_UncompressedSize => 22;
+ use constant Offset_FilenameLength => 26;
+ use constant Offset_ExtraFieldLength => 27;
+ use constant Offset_Filename => 30;
+
sub new
{
my $class = shift ;
@@ -6647,7 +6764,7 @@ sub nibbles
my $existingEntry = $self->{byName}{$filename} ;
- my $endSurfaceArea = $payloadOffset + ($localEntry->compressedSize //0) ;
+ my $endSurfaceArea = $payloadOffset + ($localEntry->compressedSize // 0) ;
if ($existingEntry)
{