Image-Magick-Safer

 view release on metacpan or  search on metacpan

META.json  view on Meta::CPAN

         "requires" : {
            "File::LibMagic" : "1.15"
         }
      }
   },
   "release_status" : "stable",
   "resources" : {
      "bugtracker" : {
         "web" : "https://github.com/Humanstate/image-magick-safer/issues"
      },
      "homepage" : "https://metacpan.org/module/Image::Magick::Safer",
      "license" : [
         "http://dev.perl.org/licenses/"
      ],
      "repository" : {
         "url" : "https://github.com/Humanstate/image-magick-safer"
      }
   },
   "version" : "0.08",
   "x_serialization_backend" : "JSON::PP version 4.16"
}

META.yml  view on Meta::CPAN

  version: '1.4'
name: Image-Magick-Safer
no_index:
  directory:
    - t
    - inc
requires:
  File::LibMagic: '1.15'
resources:
  bugtracker: https://github.com/Humanstate/image-magick-safer/issues
  homepage: https://metacpan.org/module/Image::Magick::Safer
  license: http://dev.perl.org/licenses/
  repository: https://github.com/Humanstate/image-magick-safer
version: '0.08'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

Makefile.PL  view on Meta::CPAN

#!perl

use strict;
use warnings;

use ExtUtils::MakeMaker;

WriteMakefile(
    NAME             => 'Image::Magick::Safer',
    VERSION_FROM     => 'lib/Image/Magick/Safer.pm',
    AUTHOR           => 'Lee Johnson <leejo@cpan.org>',
    LICENSE          => 'perl',
    PREREQ_PM => {
        'File::LibMagic' => 1.15,
    },
    BUILD_REQUIRES => {
    },
    META_MERGE => {
        resources => {
            license    => 'http://dev.perl.org/licenses/',
            homepage   => 'https://metacpan.org/module/Image::Magick::Safer',
            bugtracker => 'https://github.com/Humanstate/image-magick-safer/issues',
            repository => 'https://github.com/Humanstate/image-magick-safer'
        },
    },
    test => {
        RECURSIVE_TEST_FILES => 1,
    },
);

# vim: ts=4:sw=4:et

README.md  view on Meta::CPAN

# NAME

Image::Magick::Safer - Wrap Image::Magick Read method to check magic bytes

<div>

    <a href='https://travis-ci.org/Humanstate/image-magick-safer?branch=master'><img src='https://travis-ci.org/Humanstate/image-magick-safer.svg?branch=master' alt='Build Status' /></a>
    <a href='https://coveralls.io/r/Humanstate/image-magick-safer?branch=master'><img src='https://coveralls.io/repos/Humanstate/image-magick-safer/badge.png?branch=master' alt='Coverage Status' /></a>
</div>

# VERSION

0.07

# SYNOPSIS

        use Image::Magick::Safer;

        # functions just like Image::Magick but wraps the Read method
        # to check the magic bytes of any images using File::LibMagic
        my $magick = Image::Magick::Safer->new;

        # if any @files have a MIME type that looks questionable then
        # $e will be populated
        if ( my $e = $magick->Read( @files ) ) {
                # bail out, unsafe to continue
                ....
        }

# DESCRIPTION

Image::Magick::Safer is a drop in wrapper around Image::Magick, it adds a
magic byte check to the `Read` method to check the file MIME type using
[File::LibMagic](https://metacpan.org/pod/File::LibMagic). If a file looks questionable then it will prevent the file
being passed to the real Image::Magick::Read method and return an error.
If a file cannot be opened, because it does not exist or it is prefixed
with a pipe, an error will also be returned.

You can replace any calls to `Image::Magick` with `Image::Magick::Safer`
and the functionality will be retained with the added Read protection. The
aliases for `Read` will also be made safe.

If you need to override the default MIME types then you can set the modules
`$Image::Magick::Safer::Unsafe` hash to something else or add extra types:

        # add SVG check to the defaults
        $Image::Magick::Safer::Unsafe->{'image/svg+xml'} = 1;

The default MIME types considered unsafe are as follows:

        text/plain
        application/x-compress
        application/x-compressed
        application/gzip
        application/bzip2
        application/x-bzip2
        application/x-gzip

README.md  view on Meta::CPAN

that cannot be found.

Note that i make **NO GUARANTEE** that this will fix and/or protect you from
exploits, it's just another safety check. You should update to the latest
version of ImageMagick to protect yourself against potential exploits.

Also note that to install the [File::LibMagic](https://metacpan.org/pod/File::LibMagic) module you will need to have
both the library (libmagic.so) and the header file (magic.h). See the perldoc
for [File::LibMagic](https://metacpan.org/pod/File::LibMagic) for more information.

# WHY ISN'T THIS A PATCH IN Image::Magick?

Image::Magick moves at a glacial pace, and involves a 14,000 line XS file. No
thanks. This will probably get patched in the next version, so for the time
being this module exists.

# KNOWN BUGS

DOES NOT WORK with BSD 10.1 and 7.0.1 and i can't figure out why. If you can
figure out why then please submit a pull request. This is possibly some libmagic
weirdness going on.

# SEE ALSO

[Image::Magick](https://metacpan.org/pod/Image::Magick) - the library this module wraps

[https://www.imagemagick.org](https://www.imagemagick.org) - ImageMagick

[https://imagetragick.com/](https://imagetragick.com/) - ImageMagick exploits

[http://permalink.gmane.org/gmane.comp.security.oss.general/19669](http://permalink.gmane.org/gmane.comp.security.oss.general/19669) -
GraphicsMagick and ImageMagick popen() shell vulnerability via filename

# AUTHOR

lib/Image/Magick/Safer.pm  view on Meta::CPAN

package Image::Magick::Safer;

=head1 NAME

Image::Magick::Safer - Wrap Image::Magick Read method to check magic bytes

=for html
<a href='https://travis-ci.org/Humanstate/image-magick-safer?branch=master'><img src='https://travis-ci.org/Humanstate/image-magick-safer.svg?branch=master' alt='Build Status' /></a>
<a href='https://coveralls.io/r/Humanstate/image-magick-safer?branch=master'><img src='https://coveralls.io/repos/Humanstate/image-magick-safer/badge.png?branch=master' alt='Coverage Status' /></a>

=head1 VERSION

0.08

=head1 SYNOPSIS

	use Image::Magick::Safer;

	# functions just like Image::Magick but wraps the Read method
	# to check the magic bytes of any images using File::LibMagic
	my $magick = Image::Magick::Safer->new;

	# if any @files have a MIME type that looks questionable then
	# $e will be populated
	if ( my $e = $magick->Read( @files ) ) {
		# bail out, unsafe to continue
		....
	}

=head1 DESCRIPTION

Image::Magick::Safer is a drop in wrapper around Image::Magick, it adds a
magic byte check to the C<Read> method to check the file MIME type using
L<File::LibMagic>. If a file looks questionable then it will prevent the file
being passed to the real Image::Magick::Read method and return an error.
If a file cannot be opened, because it does not exist or it is prefixed
with a pipe, an error will also be returned.

You can replace any calls to C<Image::Magick> with C<Image::Magick::Safer>
and the functionality will be retained with the added Read protection. The
aliases for C<Read> will also be made safe.

If you need to override the default MIME types then you can set the modules
C<$Image::Magick::Safer::Unsafe> hash to something else or add extra types:

	# add SVG check to the defaults
	$Image::Magick::Safer::Unsafe->{'image/svg+xml'} = 1;

The default MIME types considered unsafe are as follows:

	text/plain
	application/x-compress
	application/x-compressed
	application/gzip
	application/bzip2
	application/x-bzip2
	application/x-gzip

lib/Image/Magick/Safer.pm  view on Meta::CPAN

that cannot be found.

Note that i make B<NO GUARANTEE> that this will fix and/or protect you from
exploits, it's just another safety check. You should update to the latest
version of ImageMagick to protect yourself against potential exploits.

Also note that to install the L<File::LibMagic> module you will need to have
both the library (libmagic.so) and the header file (magic.h). See the perldoc
for L<File::LibMagic> for more information.

=head1 WHY ISN'T THIS A PATCH IN Image::Magick?

Image::Magick moves at a glacial pace, and involves a 14,000 line XS file. No
thanks. This will probably get patched in the next version, so for the time
being this module exists.

=cut

use strict;
use warnings;

use parent 'Image::Magick';
use File::LibMagic;

our $VERSION = '0.08';

# imagemagick can automatically uncompress archive files so there's another
# attack vector in having an exploit image zipped up, so just checking for
# text/plain isn't enough
$Image::Magick::Safer::Unsafe = {
	map { $_ => 1 }
		'text/plain',
		'application/x-compress',
		'application/x-compressed',
		'application/gzip',
		'application/bzip2',
		'application/x-bzip2',
		'application/x-gzip',
		'application/x-rar',
		'application/x-z',

lib/Image/Magick/Safer.pm  view on Meta::CPAN

		#     mime_type
		if ( my $info = $magic->info_from_filename( $image ) ) {

			$info->{mime_type} = ''
				if ! defined $info->{mime_type};

			if ( $info->{mime_type} =~ /No such file or directory/i ) {
				return $info->{mime_type};
			}

			# if the mime_type is within the $Image::Magick::Safer::Unsafe
			# hash or we can't figure it out then we assume it's not a real
			# image and therefore could have an exploit within the file
			if (
				! $info->{mime_type}
				|| $Image::Magick::Safer::Unsafe->{ $info->{mime_type} }
			) {
				return "$image is of type @{[ $info->{mime_type} ]}, potentially unsafe";
			}
		} else {
			return "unable to establish mime_type for $image";
		}
	}

	# all images *seem* ok, delegate to the real Image::Magick
	return $self->SUPER::Read( @images );
}

# Image::Magick has a few aliases for the Read method
*Image::Magick::Safer::ReadImage = *Image::Magick::Safer::Read;
*Image::Magick::Safer::read      = *Image::Magick::Safer::Read;
*Image::Magick::Safer::readimage = *Image::Magick::Safer::Read;

=head1 KNOWN BUGS

DOES NOT WORK with BSD 10.1 and 7.0.1 and i can't figure out why. If you can
figure out why then please submit a pull request. This is possibly some libmagic
weirdness going on.

=head1 SEE ALSO

L<Image::Magick> - the library this module wraps

L<https://www.imagemagick.org> - ImageMagick

L<https://imagetragick.com/> - ImageMagick exploits

L<http://permalink.gmane.org/gmane.comp.security.oss.general/19669> -
GraphicsMagick and ImageMagick popen() shell vulnerability via filename

=head1 AUTHOR

t/005_safer.t  view on Meta::CPAN

#!perl

# mocking Image::Magick dependency here for testing as it may not be installed
# and it's an absolute swine to install so that's not going to work on
# cpantesters et al
BEGIN {
    $INC{'Image/Magick.pm'} ||= "mocked";
};

package Image::Magick;

sub new { bless( {},shift ); };

1;

use strict;
use warnings;

use Test::More;
use FindBin qw/ $Bin /;
use File::Spec::Functions qw/ catdir /;
use File::Basename;
use Config;

use Image::Magick::Safer;

if (
	$^O =~ /BSD/i
	&& $Config{osvers} =~ /(10\.1|7\.0\.1)/
) {
	plan skip_all => "Issues with BSD $1, see GH #2";
}

my $magick = Image::Magick::Safer->new;

# Image::Magick::Read could fail for other reasons, so we monkey patch it here
# to make sure it returns "success" (which in Image::Magick terms is void)
no warnings 'redefine';
no warnings 'once';
*Image::Magick::Read = sub ($;@) {};

# add SVG check to the defaults
$Image::Magick::Safer::Unsafe->{'image/svg+xml'} = 1;

note( "magic byte check" );

foreach my $file ( glob catdir( $Bin,"exploit","*" ) ) {

	foreach my $method ( qw/ Read ReadImage read readimage / ) {
		my $e = $magick->$method( $file );
		like(
			$e,
			qr/potentially unsafe|unable to establish/,



( run in 1.834 second using v1.01-cache-2.11-cpan-beeb90c9504 )