CHANGES 07
MANIFEST 04
MANIFEST.SKIP 01
META.json 22
META.yml 11
SIGNATURE 1519
dev/Info.pm.tmpl 22
img/xxe.svg 014
lib/Image/Info/SVG/XMLLibXMLReader.pm 33
lib/Image/Info/SVG/XMLSimple.pm 17
lib/Image/Info/SVG.pm 414
lib/Image/Info.pm 22
t/xbm.t 033
t/xpm.t 034
xt/rt118099.t 069
xt/synopsis.t 20
16 files changed (This is a version diff) 32212
@@ -1,5 +1,12 @@
 Revision history for Image::Info
 
+2016-10-01  Slaven Rezic  <slaven@rezic.de>
+
+    Release 1.38_50
+
+    Don't allow XXE (XML External Entities) processing. Addresses RT
+    #118205.
+
 2015-04-19  Slaven Rezic  <slaven@rezic.de>
 
     Release 1.38
@@ -35,6 +35,7 @@ img/test0-fuji.jpg		Testcase for RT #49546
 img/test1-fuji.jpg		Testcase for RT #49546
 img/tiny.pgm
 img/upside-down.bmp
+img/xxe.svg			Testcase for RT #118099
 img/ztxt.png			PNG example with ztxt chunk
 imgdump				Test driver script
 lib/Bundle/Image/Info/Everything.pm
@@ -80,8 +81,11 @@ t/tiff_e.t
 t/tiff_segfault.t
 t/tiny-pgm.t
 t/wbmp.t
+t/xbm.t
+t/xpm.t
 TODO
 xt/kwalitee.t
+xt/rt118099.t
 xt/strict.t
 xt/synopsis.t
 META.yml                                 Module YAML meta-data (added by MakeMaker)
@@ -7,6 +7,7 @@ pm_to_blib
 ^\.git/
 ^\.prove\z
 ^\.travis\.yml\z
+^appveyor\.yml\z
 ^MYMETA.json$
 ^MYMETA.yml$
 # Temporarily, until Module::Install is fixed (0.93 is broken)
@@ -48,11 +48,11 @@
          }
       }
    },
-   "release_status" : "stable",
+   "release_status" : "testing",
    "resources" : {
       "repository" : {
          "url" : "git://github.com/eserte/image-info.git"
       }
    },
-   "version" : "1.38"
+   "version" : "1.38_50"
 }
@@ -32,4 +32,4 @@ requires:
   perl: 5.006
 resources:
   repository: git://github.com/eserte/image-info.git
-version: 1.38
+version: 1.38_50
@@ -1,5 +1,5 @@
 This file contains message digests of all files listed in MANIFEST,
-signed via the Module::Signature module, version 0.73.
+signed via the Module::Signature module, version 0.79.
 
 To verify the content in this distribution, first make sure you have
 Module::Signature installed, then type:
@@ -15,16 +15,16 @@ not run its Makefile.PL or Build.PL.
 Hash: SHA1
 
 SHA1 c82d6187bf83f92dcd4a4ba9ab8341c3edcc094a .gitignore
-SHA1 93e03daefbe0bfec3c418f671d6ee7206e17e47d CHANGES
+SHA1 b8d32c47657484a8f00aeb166c39b626413e5ed8 CHANGES
 SHA1 517bff80bcf518746150086148acaf8cfa37a17d CREDITS
-SHA1 8b13b87882e8e171d87da8495e868415b49a15c9 MANIFEST
-SHA1 396a95962b71b01b3032a31ff5f12a33984c2644 MANIFEST.SKIP
-SHA1 3660da7a8d216cff8b7e11c91ea3ce2bc34b037e META.json
-SHA1 4ebc7bd648c8aa6dd8aedd49a4b233084dbeaf1f META.yml
+SHA1 cdfc42491a4cdaa42af14d791cb39e775f719de7 MANIFEST
+SHA1 7508fd127843a69e30ba4f58f4b4e769f20d49e2 MANIFEST.SKIP
+SHA1 982d477a6fb375bf80e4050d20da36a391cbee8d META.json
+SHA1 90407b78357a66598f6bca877615e2fd2419e941 META.yml
 SHA1 fd14642d591e132078fc91a8e54a4a4fed927f46 Makefile.PL
 SHA1 0184503d850cb0d34d0cfe26bd5af84d4cf97dbc README
 SHA1 36ea8eabe5ac80acc416411aae77b60e1480de1b TODO
-SHA1 0e763171d566addcfff34c7309979a27326b33dd dev/Info.pm.tmpl
+SHA1 741dc9d13bb3d500d4e1dfbfc4b7ca6e6c054b72 dev/Info.pm.tmpl
 SHA1 c11e7408bd16d35b2116ee47eaac4f08266be902 dev/build.pl
 SHA1 38b386e67725abff64ea00abb8e92c46f38e2f59 exifdump
 SHA1 0e2ed058a8e6b748c639b08064f6782c4f51d643 img/bad-exif-1.jpg
@@ -58,6 +58,7 @@ SHA1 2d2565cc6074fc831924dd378a4a579415505cfb img/test0-fuji.jpg
 SHA1 603d6eed47d8822cf4bae53bafa18b8044c035a6 img/test1-fuji.jpg
 SHA1 d33e338aa7a45dc6fa4cefcdd376916e2267f3f9 img/tiny.pgm
 SHA1 e403a1722e5d26002d8afbf29bd497c60f8ee05f img/upside-down.bmp
+SHA1 5023229b3010b9e9ec663ce10e29bd3cbe5486c8 img/xxe.svg
 SHA1 684ef627299906409c258ed2a66990a26dd13794 img/ztxt.png
 SHA1 376cc1bd8424b1123f0073df81ce5433be4df58e imgdump
 SHA1 b28345eb7918c00c9e8190b9b5ebabb326a84d91 lib/Bundle/Image/Info/Everything.pm
@@ -65,16 +66,16 @@ SHA1 4a2ca6a6407e82ba0de7ccde961771365b5928d8 lib/Bundle/Image/Info/PNG.pm
 SHA1 777028fc6271fc140b361904b7ff29053d404144 lib/Bundle/Image/Info/SVG.pm
 SHA1 ebdf279c647010c1fa17b0003238debdc3e1f80b lib/Bundle/Image/Info/XBM.pm
 SHA1 df3482b8e4a6c7b140ebbb1dc0ab56dc60a8e7e7 lib/Bundle/Image/Info/XPM.pm
-SHA1 68d6cc00378159751743e38f955927fe68d5a948 lib/Image/Info.pm
+SHA1 9e6ecc0afed2716b367af226851145254506c1a5 lib/Image/Info.pm
 SHA1 6991a957bacde9d3b873184d90d910d083f5f403 lib/Image/Info/BMP.pm
 SHA1 0996bffe073126dceb72c04dc5b3002c78c24d56 lib/Image/Info/GIF.pm
 SHA1 fb628cb66c566ab66a1b7953a1bb29cfde1c79c7 lib/Image/Info/ICO.pm
 SHA1 cb662c31e3429214bdc9f1e7b65fac2c4198f689 lib/Image/Info/JPEG.pm
 SHA1 df4bd657aae33fe5ef9cbad6546674ec2de9d53f lib/Image/Info/PNG.pm
 SHA1 1b69276ab386d1795469773d6af70ff510c15feb lib/Image/Info/PPM.pm
-SHA1 0de5432c7e9100ee06b6da29ac25114e3a07a783 lib/Image/Info/SVG.pm
-SHA1 e2c4f7ff2bba5a50e2de64e5b43d78fd44b00d6c lib/Image/Info/SVG/XMLLibXMLReader.pm
-SHA1 827501c639b7db47dac2a7e050df6bf887f08161 lib/Image/Info/SVG/XMLSimple.pm
+SHA1 67fcd8cd40c30bf4fb995e232fd51bc261607bd8 lib/Image/Info/SVG.pm
+SHA1 c8aa83ad22ff1684a40bae3f37fccc358842b1d1 lib/Image/Info/SVG/XMLLibXMLReader.pm
+SHA1 c1fe64b6ad5faeeab7d690cd44fd0feaf56952ee lib/Image/Info/SVG/XMLSimple.pm
 SHA1 7ed4635fb08eaa8099f0b17ab25c62aabc52437f lib/Image/Info/TIFF.pm
 SHA1 ed6fde11752d6450756c815b2dca25f9ad1740d9 lib/Image/Info/WBMP.pm
 SHA1 5c896ca4cc89715527f4207bae3c1e3c7a26286a lib/Image/Info/XBM.pm
@@ -99,13 +100,16 @@ SHA1 76e7e08522799dc0f39896c8925aff1d42b09da4 t/tiff_e.t
 SHA1 c08bf003a891603d6f5f098425a70680a699e31a t/tiff_segfault.t
 SHA1 0a5f7c518af4b72fd76fa8def1f51e8691727bdd t/tiny-pgm.t
 SHA1 7d7708b0392491f75eb9b68289469c1a488453ca t/wbmp.t
+SHA1 cea87cb4a68f31dd245d911d0c43b581939c3f34 t/xbm.t
+SHA1 982933644977d3879d1b2a269bdfb1ef45723481 t/xpm.t
 SHA1 4396e44dfbfe4c2ff26d115bc0eadbce507b7bf0 xt/kwalitee.t
+SHA1 14f51fc143d06dad6d47a810596799652928221e xt/rt118099.t
 SHA1 6ef907e37bb4840f96f8d5b16b47991806f21f31 xt/strict.t
-SHA1 40f46c65b4865610c38eeb3cf05482c53e058180 xt/synopsis.t
+SHA1 776c2c96e3155a622221cb1c4989579a652452d7 xt/synopsis.t
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
-iQBVAwUBVTSHQ79kDNo3Bl+NAQKFagH9FCG+u23Ji0qRMEtZtkEtg9YQw54JrRoE
-mQMsiLIavzZLijrOBxobPzLjpztrcEi9fRAZI9iFyUYGIfqRQWnL9A==
-=6Aux
+iQBVAwUBV+/Xh79kDNo3Bl+NAQJafwH+NhWQkrxt33QbVanjmudO/nB4wvZg+Eox
+GEkmnIXruab/MzuYS9NxqTKycsOESF1DTD2B8Qq+gqYK93i1zQ3sHA==
+=rrGD
 -----END PGP SIGNATURE-----
@@ -13,12 +13,12 @@ package Image::Info;
 # modify it under the same terms as Perl v5.8.8 itself.
 #
 # Previously maintained by Tels - (c) 2006 - 2008.
-# Currently maintained by Slaven Rezic - (c) 2008 - 2015.
+# Currently maintained by Slaven Rezic - (c) 2008 - 2016.
 
 use strict;
 use vars qw($VERSION @EXPORT_OK);
 
-$VERSION = '1.38';
+$VERSION = '1.38_50';
 
 require Exporter;
 *import = \&Exporter::import;
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE foo [  
+   <!ELEMENT foo ANY >
+   <!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
+<svg
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   version="1.0"
+   width="864"
+   height="648">
+  <path
+     d="M 432,0 L 594,0 L 594,36 L 432,36 L 432,0 z"/>
+  &xxe;
+</svg>
@@ -4,7 +4,7 @@
 # $Id: Image_Info_SVG_LibXML.pm,v 1.2 2008/11/22 14:34:16 eserte Exp eserte $
 # Author: Slaven Rezic
 #
-# Copyright (C) 2008,2009 Slaven Rezic. All rights reserved.
+# Copyright (C) 2008,2009,2016 Slaven Rezic. All rights reserved.
 # This package is free software; you can redistribute it and/or
 # modify it under the same terms as Perl itself.
 #
@@ -16,7 +16,7 @@ package Image::Info::SVG::XMLLibXMLReader;
 
 use strict;
 use vars qw($VERSION);
-$VERSION = '1.04';
+$VERSION = '1.05';
 
 use XML::LibXML::Reader;
 
@@ -30,7 +30,7 @@ sub process_file {
 	push(@warnings, @_);
     };
 
-    my $reader = XML::LibXML::Reader->new(IO => $source, load_ext_dtd => 0)
+    my $reader = XML::LibXML::Reader->new(IO => $source, load_ext_dtd => 0, expand_entities => 0)
 	or die "Cannot read SVG from handle '$source'";
     while($reader->read) {
 	last if $reader->nodeType == XML_READER_TYPE_ELEMENT;
@@ -1,6 +1,6 @@
 package Image::Info::SVG::XMLSimple;
 
-$VERSION = '1.04';
+$VERSION = '1.05';
 
 use strict;
 no strict 'refs';
@@ -36,6 +36,12 @@ sub process_file {
 	push(@warnings, @_);
     };
 
+    # XML::SAX::PurePerl is the only SAX parser which is not capable
+    # of expanding external entities, so it's the only one not
+    # vulnerable against XXE processing. On the other hand,
+    # XML::SAX::PurePerl is probably the slowest parser, but for
+    # speed one should use XML::LibXML instead.
+    local $XML::Simple::PREFERRED_PARSER = 'XML::SAX::PurePerl';
     $xs = XML::Simple->new;
     $img = $xs->XMLin($imgdata);
 
@@ -3,7 +3,7 @@
 #
 # Author: Slaven Rezic
 #
-# Copyright (C) 2009,2011 Slaven Rezic. All rights reserved.
+# Copyright (C) 2009,2011,2016 Slaven Rezic. All rights reserved.
 # This package is free software; you can redistribute it and/or
 # modify it under the same terms as Perl itself.
 #
@@ -12,7 +12,7 @@ package Image::Info::SVG;
 
 use strict;
 use vars qw($VERSION @PREFER_MODULE $USING_MODULE);
-$VERSION = '2.02';
+$VERSION = '2.03';
 
 @PREFER_MODULE = qw(Image::Info::SVG::XMLLibXMLReader
 		    Image::Info::SVG::XMLSimple
@@ -100,7 +100,7 @@ This module requires either L<XML::LibXML::Reader> or L<XML::Simple>.
 
 Previous versions (until Image-Info-1.28) used L<XML::Simple> as the
 underlying parser. Since Image-Info-1.29 the default parser is
-L<XML::LibXML::Reader> (which is much more faster, memory-efficient,
+L<XML::LibXML::Reader> which is much more faster, memory-efficient,
 and does not rely on regular expressions for some aspects of XML
 parsing. If for some reason you need the old parser, you can force it
 by setting the variable C<@Image::Info::SVG::PREFER_MODULE> as early
@@ -112,9 +112,19 @@ as possible:
 The variable C<$Image::Info::SVG::USING_MODULE> can be queried to see
 which parser is in use (after B<Image::Info::SVG> is required).
 
+Since 1.38_50 processing of XML external entities (XXE) is not done
+anymore for security reasons in both backends
+(B<Image::Info::SVG::XMLLibXMLReader> and
+B<Image::Info::SVG::XMLSimple>). Controlling XXE processing behavior
+in B<XML::Simple> is not really possible (see
+L<https://rt.cpan.org/Ticket/Display.html?id=83794>), so as a
+workaround the underlying SAX parser is fixed to L<XML::SAX::PurePerl>
+which is uncapable of processing external entities E<0x2014> but
+unfortunately it is also a slow parser.
+
 =head1 SEE ALSO
 
-L<Image::Info>, L<XML::LibXML::Reader>, L<XML::Simple>
+L<Image::Info>, L<XML::LibXML::Reader>, L<XML::Simple>, L<XML::SAX::PurePerl>
 
 =head1 NOTES
 
@@ -13,12 +13,12 @@ package Image::Info;
 # modify it under the same terms as Perl v5.8.8 itself.
 #
 # Previously maintained by Tels - (c) 2006 - 2008.
-# Currently maintained by Slaven Rezic - (c) 2008 - 2015.
+# Currently maintained by Slaven Rezic - (c) 2008 - 2016.
 
 use strict;
 use vars qw($VERSION @EXPORT_OK);
 
-$VERSION = '1.38';
+$VERSION = '1.38_50';
 
 require Exporter;
 *import = \&Exporter::import;
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+
+use Test::More;
+BEGIN
+   {
+   plan tests => 8;
+   chdir 't' if -d 't';
+   use lib '../lib';
+   use lib '../blib';
+   use_ok ("Image::Info") or die $@;
+   };
+
+use Image::Info qw(image_info dim);
+
+SKIP: {
+skip 'Image::Xbm needed for the test', 7 unless eval { require Image::Xbm };
+
+my $i = image_info("../img/test.xbm")
+ || die ("Couldn't read test.xbm: $!");
+
+# use Data::Dumper; diag Dumper($i), "\n";
+
+is ($i->{BitsPerSample}, 1, 'BitsPerSample');
+is ($i->{SamplesPerPixel}, 1, 'SamplesPerPixel');
+is ($i->{file_media_type}, 'image/x-xbitmap', 'media type');
+is ($i->{ColorTableSize}, 2, '2 colors');
+is ($i->{color_type}, 'Grey', 'color_type');
+is ($i->{file_ext}, 'xbm', 'file_ext');
+
+is (dim($i), '6x6', 'dim()');
+}
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+
+use Test::More;
+BEGIN
+   {
+   plan tests => 9;
+   chdir 't' if -d 't';
+   use lib '../lib';
+   use lib '../blib';
+   use_ok ("Image::Info") or die $@;
+   };
+
+use Image::Info qw(image_info dim);
+
+SKIP: {
+skip 'Image::Xpm needed for the test', 8 unless eval { require Image::Xpm };
+
+my $i = image_info("../img/test.xpm")
+ || die ("Couldn't read test.xpm: $!");
+
+# use Data::Dumper; print Dumper($i), "\n";
+
+is ($i->{ColorResolution}, 8, 'ColorResoltuion');
+is ($i->{BitsPerSample}, 8, 'BitsPerSample');
+is ($i->{SamplesPerPixel}, 1, 'SamplesPerPixel');
+is ($i->{file_media_type}, 'image/x-xpixmap', 'media type');
+is ($i->{ColorTableSize}, 2, '2 colors');
+is ($i->{color_type}, 'Indexed-RGB', 'color_type');
+is ($i->{file_ext}, 'xpm', 'file_ext');
+
+is (dim($i), '127x13', 'dim()');
+}
@@ -0,0 +1,69 @@
+#!/usr/bin/perl -w
+# -*- cperl -*-
+
+#
+# Author: Slaven Rezic
+#
+
+use strict;
+use FindBin;
+use IPC::Run 'run';
+use List::Util 'sum';
+use Test::More;
+
+plan skip_all => "Works only on linux (using strace)" if $^O ne 'linux';
+
+my %impl2opts =
+    (
+     'Image::Info::SVG::XMLSimple' =>
+     [
+      {XML_SAX_Parser => 'XML::Parser'},
+      {XML_SAX_Parser => 'XML::SAX::Expat'},
+      {XML_SAX_Parser => 'XML::SAX::ExpatXS'},
+      {XML_SAX_Parser => 'XML::SAX::PurePerl'},
+      {XML_SAX_Parser => 'XML::LibXML::SAX::Parser'},
+      {XML_SAX_Parser => 'XML::LibXML::SAX'},
+     ],
+     'Image::Info::SVG::XMLLibXMLReader' => [{}],
+    );
+
+plan tests => 2 * sum map { scalar @$_ } values(%impl2opts);
+
+for my $impl (keys %impl2opts) {
+    my $testname = $impl;
+    my @opts = @{ $impl2opts{$impl} };
+    for my $opt (@opts) {
+	my $testname = $testname . (%$opt ? ", " . join(", ", map { "$_ => $opt->{$_}" } keys %$opt) : '');
+	my @cmd =
+	    (
+	     $^X, "-I$FindBin::RealBin/../lib", '-MImage::Info=image_info', '-e',
+	     ($opt->{XML_SAX_Parser} ? 'require XML::Simple; $XML::Simple::PREFERRED_PARSER = shift; ' : '') .
+	     '@Image::Info::SVG::PREFER_MODULE=shift; my $info = image_info(shift); die $info->{error} if $info->{error};',
+	     ($opt->{XML_SAX_Parser} ? $opt->{XML_SAX_Parser} : ()),
+	     $impl, "$FindBin::RealBin/../img/xxe.svg",
+	    );
+	{
+	    my $stderr;
+	    ok run(\@cmd, '2>', \$stderr), "Run @cmd"
+		or diag $stderr;
+	}
+	{
+	    my $success = run(["strace", "-eopen,stat", @cmd], '2>', \my $strace);
+	    if (!$success) {
+		if (($opt->{XML_SAX_Parser}||'') eq 'XML::SAX::ExpatXS') {
+		    # ignore error
+		} else {
+		    die "Error running @cmd with strace";
+		}
+	    }
+	    my @matching_lines = $strace =~ m{.*/etc/passwd.*}g;
+	    is scalar(@matching_lines), 0, "No XXE with $testname"
+		or diag explain \@matching_lines;
+	}
+    }
+}
+
+done_testing;
+
+
+__END__
@@ -10,5 +10,3 @@ use Test::More;
 eval "use Test::Synopsis";
 plan skip_all => "Test::Synopsis required for testing" if $@;
 all_synopsis_ok();
-
-done_testing;