]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/commitdiff
useragent: Don't allow non-HTTP protocols to be used
authorSimon McVittie <smcv@debian.org>
Sun, 10 Feb 2019 16:30:07 +0000 (16:30 +0000)
committerSimon McVittie <smcv@debian.org>
Tue, 26 Feb 2019 22:57:30 +0000 (22:57 +0000)
This prevents the aggregate plugin from being used to read the contents
of local files via file:/// URLs.

Signed-off-by: Simon McVittie <smcv@debian.org>
IkiWiki.pm
t/aggregate-file.t [new file with mode: 0755]
t/noparanoia/LWPx/ParanoidAgent.pm [new file with mode: 0644]
t/secret.rss [new file with mode: 0644]

index f916a566db4c65760b3f4203c89095887d27fcf5..faa442ee4d13f5eff07c1560cf4d11b7c877cc88 100644 (file)
@@ -2455,6 +2455,7 @@ sub useragent () {
                cookie_jar => $config{cookiejar},
                env_proxy => 1,         # respect proxy env vars
                agent => $config{useragent},
+               protocols_allowed => [qw(http https)],
        );
 }
 
diff --git a/t/aggregate-file.t b/t/aggregate-file.t
new file mode 100755 (executable)
index 0000000..f00743d
--- /dev/null
@@ -0,0 +1,173 @@
+#!/usr/bin/perl
+use utf8;
+use warnings;
+use strict;
+
+use Encode;
+use Test::More;
+
+BEGIN {
+       plan(skip_all => "CGI not available")
+               unless eval q{
+                       use CGI qw();
+                       1;
+               };
+
+       plan(skip_all => "IPC::Run not available")
+               unless eval q{
+                       use IPC::Run qw(run);
+                       1;
+               };
+
+       use_ok('IkiWiki');
+       use_ok('YAML::XS');
+}
+
+# We check for English error messages
+$ENV{LC_ALL} = 'C';
+
+use Cwd qw(getcwd);
+use Errno qw(ENOENT);
+
+my $installed = $ENV{INSTALLED_TESTS};
+
+my @command;
+if ($installed) {
+       @command = qw(ikiwiki --plugin inline);
+}
+else {
+       ok(! system("make -s ikiwiki.out"));
+       @command = ("perl", "-I".getcwd."/blib/lib", './ikiwiki.out',
+               '--underlaydir='.getcwd.'/underlays/basewiki',
+               '--set', 'underlaydirbase='.getcwd.'/underlays',
+               '--templatedir='.getcwd.'/templates');
+}
+
+sub write_old_file {
+       my $name = shift;
+       my $dir = shift;
+       my $content = shift;
+       writefile($name, $dir, $content);
+       ok(utime(333333333, 333333333, "$dir/$name"));
+}
+
+sub write_setup_file {
+       my %params = @_;
+       my %setup = (
+               wikiname => 'this is the name of my wiki',
+               srcdir => getcwd.'/t/tmp/in',
+               destdir => getcwd.'/t/tmp/out',
+               url => 'http://example.com',
+               cgiurl => 'http://example.com/cgi-bin/ikiwiki.cgi',
+               cgi_wrapper => getcwd.'/t/tmp/ikiwiki.cgi',
+               cgi_wrappermode => '0751',
+               add_plugins => [qw(aggregate)],
+               disable_plugins => [qw(emailauth openid passwordauth)],
+               aggregate_webtrigger => 1,
+       );
+       if ($params{without_paranoia}) {
+               $setup{libdirs} = [getcwd.'/t/noparanoia'];
+       }
+       unless ($installed) {
+               $setup{ENV} = { 'PERL5LIB' => getcwd.'/blib/lib' };
+       }
+       writefile("test.setup", "t/tmp",
+               "# IkiWiki::Setup::Yaml - YAML formatted setup file\n" .
+               Dump(\%setup));
+}
+
+sub thoroughly_rebuild {
+       ok(unlink("t/tmp/ikiwiki.cgi") || $!{ENOENT});
+       ok(! system(@command, qw(--setup t/tmp/test.setup --rebuild --wrappers)));
+}
+
+sub run_cgi {
+       my (%args) = @_;
+       my ($in, $out);
+       my $method = $args{method} || 'GET';
+       my $environ = $args{environ} || {};
+       my $params = $args{params} || { do => 'prefs' };
+
+       my %defaults = (
+               SCRIPT_NAME     => '/cgi-bin/ikiwiki.cgi',
+               HTTP_HOST       => 'example.com',
+       );
+
+       my $cgi = CGI->new($args{params});
+       my $query_string = $cgi->query_string();
+       diag $query_string;
+
+       if ($method eq 'POST') {
+               $defaults{REQUEST_METHOD} = 'POST';
+               $in = $query_string;
+               $defaults{CONTENT_LENGTH} = length $in;
+       } else {
+               $defaults{REQUEST_METHOD} = 'GET';
+               $defaults{QUERY_STRING} = $query_string;
+       }
+
+       my %envvars = (
+               %defaults,
+               %$environ,
+       );
+       run(["./t/tmp/ikiwiki.cgi"], \$in, \$out, init => sub {
+               map {
+                       $ENV{$_} = $envvars{$_}
+               } keys(%envvars);
+       });
+
+       return decode_utf8($out);
+}
+
+sub test {
+       my $content;
+
+       ok(! system(qw(rm -rf t/tmp)));
+       ok(! system(qw(mkdir t/tmp)));
+
+       write_old_file('aggregator.mdwn', 't/tmp/in',
+               '[[!aggregate name="ssrf" url="file://'.getcwd.'/t/secret.rss"]]'
+               .'[[!inline pages="internal(aggregator/*)"]]');
+
+       write_setup_file();
+       thoroughly_rebuild();
+
+       $content = run_cgi(
+               method => 'GET',
+               params => {
+                       do => 'aggregate_webtrigger',
+               },
+       );
+       unlike($content, qr{creating new page});
+       unlike($content, qr{Secrets});
+       ok(! -e 't/tmp/in/.ikiwiki/transient/aggregator/ssrf');
+       ok(! -e 't/tmp/in/.ikiwiki/transient/aggregator/ssrf/Secrets_go_here._aggregated');
+
+       thoroughly_rebuild();
+       $content = readfile('t/tmp/out/aggregator/index.html');
+       unlike($content, qr{Secrets});
+
+       diag('Trying test again with LWPx::ParanoidAgent disabled');
+
+       write_setup_file(without_paranoia => 1);
+       thoroughly_rebuild();
+
+       $content = run_cgi(
+               method => 'GET',
+               params => {
+                       do => 'aggregate_webtrigger',
+               },
+       );
+       unlike($content, qr{creating new page});
+       unlike($content, qr{Secrets});
+       ok(! -e 't/tmp/in/.ikiwiki/transient/aggregator/ssrf');
+       ok(! -e 't/tmp/in/.ikiwiki/transient/aggregator/ssrf/Secrets_go_here._aggregated');
+
+       thoroughly_rebuild();
+       $content = readfile('t/tmp/out/aggregator/index.html');
+       unlike($content, qr{Secrets});
+}
+
+test();
+
+done_testing();
diff --git a/t/noparanoia/LWPx/ParanoidAgent.pm b/t/noparanoia/LWPx/ParanoidAgent.pm
new file mode 100644 (file)
index 0000000..751e80c
--- /dev/null
@@ -0,0 +1,2 @@
+# make import fail
+0;
diff --git a/t/secret.rss b/t/secret.rss
new file mode 100644 (file)
index 0000000..11202e9
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<rss version="2.0">
+<channel>
+<title>Secrets go here</title>
+<description>Secrets go here</description>
+<item>
+  <title>Secrets go here</title>
+  <description>Secrets go here</description>
+</item>
+</channel>
+</rss>