App-zipdetails

 view release on metacpan or  search on metacpan

bin/zipdetails  view on Meta::CPAN


    # Slip Vulnerability with use of ".." in a relative path
    # https://security.snyk.io/research/zip-slip-vulnerability
    return ["Use of '..' in filename is a Zip Slip Vulnerability",
            "See https://security.snyk.io/research/zip-slip-vulnerability" ]
        if $filename =~ m#^\.\./# || $filename =~ m#/\.\./# || $filename =~ m#/\.\.# ;

    # Cannot have "." or ".." as the full filename
    return "Use of current-directory filename '.' may not unzip correctly"
        if $filename eq '.' ;

    return "Use of parent-directory filename '..' may not unzip correctly"
        if $filename eq '..' ;

    # Portability (mostly with Windows)

    {
        # see https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
        state $badDosFilename = join '|', map { quotemeta }
                                qw(CON  PRN  AUX  NUL
                                COM1 COM2 COM3 COM4 COM5 COM6 COM7 COM8 COM9
                                LPT1 LPT2 LPT3 LPT4 LPT5 LPT6 LPT7 LPT8 LPT9
                                ) ;

        # if $filename contains any invalid codepoints, we will get a warning like this
        #
        #   Operation "pattern match (m//)" returns its argument for non-Unicode code point
        #
        # so silence it for now.

        no warnings;

        return "Portability Issue: '$1' is a reserved Windows device name"
            if $filename =~ /^($badDosFilename)$/io ;

        # Can't have the device name with an extension either
        return "Portability Issue: '$1' is a reserved Windows device name"
            if $filename =~ /^($badDosFilename)\./io ;
    }

    state $illegal_windows_chars = join '|', map { quotemeta } qw( < > : " | ? * );
    return "Portability Issue: Windows filename cannot contain '$1'"
        if  $filename =~ /($illegal_windows_chars)/o ;

    return "Portability Issue: Null character '\\x00' is not allowed in a Windows or Linux filename"
        if  $filename =~ /\x00/ ;

    return sprintf "Portability Issue: Control character '\\x%02X' is not allowed in a Windows filename", ord($1)
        if  $filename =~ /([\x00-\x1F])/ ;

    return undef;
}

sub getOutputFilename
{
    my $raw_filename = shift;
    my $LanguageEncodingFlag = shift;
    my $message = shift // "Filename";

    my $filename ;
    my $decoded_filename;

    if ($raw_filename eq '')
    {
        if ($message eq 'Filename')
        {
            warning $FH->tell() ,
                "Filename ''",
                "Zero Length Filename" ;
        }

        return '', '', 0;
    }
    elsif ($opt_Redact)
    {
        return redactFilename($raw_filename), '', 0 ;
    }
    else
    {
        $decoded_filename = TextEncoding::decode($raw_filename, $message, $LanguageEncodingFlag) ;
        $filename = TextEncoding::encode($decoded_filename, $message, $LanguageEncodingFlag) ;
    }

    return $filename, $decoded_filename, $filename ne $raw_filename ;
}

sub outputFilename
{
    my $raw_filename = shift;
    my $LanguageEncodingFlag = shift;
    my $message = shift // "Filename";

    my ($filename, $decoded_filename, $modified) = getOutputFilename($raw_filename, $LanguageEncodingFlag);

    out $raw_filename, $message,  "'". $filename . "'";

    if (! $opt_Redact && TextEncoding::debugEncoding())
    {
        # use Devel::Peek;
        # print "READ     " ; Dump($raw_filename);
        # print "INTERNAL " ; Dump($decoded_filename);
        # print "OUTPUT   " ; Dump($filename);

        debug $FH->tell() - length($raw_filename),
                    "$message Encoding Change"
            if $modified ;

        # use Unicode::Normalize;
        # my $NormalizedForm ;
        # if (defined $decoded_filename)
        # {
        #     $NormalizedForm .= Unicode::Normalize::checkNFD  $decoded_filename ? 'NFD ' : '';
        #     $NormalizedForm .= Unicode::Normalize::checkNFC  $decoded_filename ? 'NFC ' : '';
        #     $NormalizedForm .= Unicode::Normalize::checkNFKD $decoded_filename ? 'NFKD ' : '';
        #     $NormalizedForm .= Unicode::Normalize::checkNFKC $decoded_filename ? 'NFKC ' : '';
        #     $NormalizedForm .= Unicode::Normalize::checkFCD  $decoded_filename ? 'FCD ' : '';
        #     $NormalizedForm .= Unicode::Normalize::checkFCC  $decoded_filename ? 'FCC ' : '';
        # }

        debug $FH->tell() - length($raw_filename),
                    "Encoding Debug for $message",
                    "Octets Read from File  [$raw_filename][" . length($raw_filename). "] [" . charDump2($raw_filename) . "]",
                    "Via Unicode Codepoints [$decoded_filename][" . length($decoded_filename) . "] [" . charDump($decoded_filename) . "]",
                    # "Unicode Normalization  $NormalizedForm",
                    "Octets Written         [$filename][" . length($filename). "] [" . charDump2($filename) . "]";
    }

    if ($message eq 'Filename' && $opt_want_warning_messages)
    {
        # Check for bad, unsafe & not portable filenames
        my $v = validateFilename($decoded_filename);

        if ($v)
        {
            my @v = ref $v eq 'ARRAY'
                        ? @$v
                        : $v;

            warning $FH->tell() - length($raw_filename),
                "Filename '$filename'",
                @v
        }
    }

    return $filename;
}

sub CentralHeader
{
    my $signature = shift ;
    my $data = shift ;
    my $startRecordOffset = shift ;

    my $cdEntryOffset = $FH->tell() - 4 ;

    ++ $CentralHeaderCount;

    print "\n";
    out $data, "CENTRAL HEADER #$CentralHeaderCount", Value_V($signature);
    my $buffer;

    need 42, Signatures::name($signature);

    out_C "Created Zip Spec", \&decodeZipVer;
    my $made_by = out_C "Created OS", \&decodeOS;
    my $extractVer = out_C "Extract Zip Spec", \&decodeZipVer;
    out_C "Extract OS", \&decodeOS;

    my ($bgp, $gpFlag) = read_v();
    my ($bcm, $compressedMethod) = read_v();

    my $cdEntry = CentralDirectoryEntry->new($cdEntryOffset);

    out $bgp, "General Purpose Flag", Value_v($gpFlag) ;
    GeneralPurposeBits($compressedMethod, $gpFlag);
    my $LanguageEncodingFlag = $gpFlag & ZIP_GP_FLAG_LANGUAGE_ENCODING ;
    $cdEntry->languageEncodingFlag($LanguageEncodingFlag) ;

    out $bcm, "Compression Method", compressionMethod($compressedMethod) ;
    info $FH->tell() - 2, "Unknown 'Compression Method' ID " . decimalHex0x($compressedMethod, 2)
        if ! defined $ZIP_CompressionMethods{$compressedMethod} ;

    my $lastMod = out_V "Modification Time", sub { LastModTime($_[0]) };

    my $crc                = out_V "CRC";
    my $compressedSize   = out_V "Compressed Size";
    my $std_compressedSize   = $compressedSize;
    my $uncompressedSize = out_V "Uncompressed Size";
    my $std_uncompressedSize = $uncompressedSize;
    my $filenameLength     = out_v "Filename Length";
    if ($filenameLength == 0)

bin/zipdetails  view on Meta::CPAN

}

1;

__END__

=head1 NAME

zipdetails - display the internal structure of zip files

=head1 SYNOPSIS

    zipdetails [options] zipfile.zip

=head1 DESCRIPTION

This program creates a detailed report on the internal structure of zip
files. For each item of metadata within a zip file the program will output

=over 5

=item the offset into the zip file where the item is located.

=item a textual representation for the item.

=item an optional hex dump of the item.

=back


The program assumes a prior understanding of the internal structure of Zip
files. You should have a copy of the zip file definition,
L<APPNOTE.TXT|https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT>,
at hand to help understand the output from this program.

=head2 Default Behaviour

By default the program expects to be given a well-formed zip file.  It will
navigate the zip file by first parsing the zip C<Central Directory> at the end
of the file.  If the C<Central Directory> is found, it will then walk
sequentially through the zip records starting at the beginning of the file.
See L<Advanced Analysis> for other processing options.

If the program finds any structural or portability issues with the zip file
it will print a message at the point it finds the issue and/or in a summary
at the end of the output report. Whilst the set of issues that can be
detected it exhaustive, don't assume that this program can find I<all> the
possible issues in a zip file - there are likely edge conditions that need
to be addressed.

If you have suggestions for use-cases where this could be enhanced please
consider creating an enhancement request (see L<"SUPPORT">).

=head3 Date & Time fields

Date/time fields found in zip files are displayed in local time. Use the
C<--utc> option to display these fields in Coordinated Universal Time (UTC).

=head3 Filenames & Comments

Filenames and comments are decoded/encoded using the default system
encoding of the host running C<zipdetails>. When the system encoding cannot
be determined C<cp437> will be used.

The exceptions are

=over 5

=item *

when the C<Language Encoding Flag> is set in the zip file, the
filename/comment fields are assumed to be encoded in UTF-8.

=item *

the definition for the metadata field implies UTF-8 charset encoding

=back

See L<"Filename Encoding Issues"> and L<Filename & Comment Encoding
Options> for ways to control the encoding of filename/comment fields.

=head2 OPTIONS

=head3 General Options

=over 5

=item C<-h>, C<--help>

Display help

=item C<--redact>

Obscure filenames and payload data in the output. Handy for the use case
where the zip files contains sensitive data that cannot be shared.

=item C<--scan>

Pessimistically scan the zip file looking for possible zip records. Can be
error-prone. For very large zip files this option is slow. Consider using
the C<--walk> option first. See L<"Advanced Analysis Options">

=item C<--utc>

By default, date/time fields are displayed in local time. Use this option to
display them in in Coordinated Universal Time (UTC).

=item C<-v>

Enable Verbose mode. See L<"Verbose Output">.

=item C<--version>

Display version number of the program and exit.

=item C<--walk>

Optimistically walk the zip file looking for possible zip records.
See L<"Advanced Analysis Options">



( run in 3.935 seconds using v1.01-cache-2.11-cpan-5837b0d9d2c )