From d712389ae3e8351c1416aa81d4b85586cf98f002 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@debian.org>
Date: Sun, 5 Oct 2014 22:56:55 +0100
Subject: [PATCH] Avoid mixed content when cgiurl is https but url is not

---
 IkiWiki.pm     | 22 +++++++++++++++++++++-
 t/relativity.t | 16 ++++++----------
 t/urlto.t      | 10 ++++++----
 3 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/IkiWiki.pm b/IkiWiki.pm
index c1518a2ae..38b91ae1d 100644
--- a/IkiWiki.pm
+++ b/IkiWiki.pm
@@ -613,7 +613,26 @@ sub checkconfig () {
 
 			$local_cgiurl = $cgiurl->path;
 
-			if ($cgiurl->scheme ne $baseurl->scheme) {
+			if ($cgiurl->scheme eq 'https' &&
+				$baseurl->scheme eq 'http') {
+				# We assume that the same content is available
+				# over both http and https, because if it
+				# wasn't, accessing the static content
+				# from the CGI would be mixed-content,
+				# which would be a security flaw.
+
+				if ($cgiurl->authority ne $baseurl->authority) {
+					# use protocol-relative URL for
+					# static content
+					$local_url = "$config{url}/";
+					$local_url =~ s{^http://}{//};
+				}
+				# else use host-relative URL for static content
+
+				# either way, CGI needs to be absolute
+				$local_cgiurl = $config{cgiurl};
+			}
+			elsif ($cgiurl->scheme ne $baseurl->scheme) {
 				# too far apart, fall back to absolute URLs
 				$local_url = "$config{url}/";
 				$local_cgiurl = $config{cgiurl};
@@ -626,6 +645,7 @@ sub checkconfig () {
 				$local_cgiurl = $config{cgiurl};
 				$local_cgiurl =~ s{^https?://}{//};
 			}
+			# else keep host-relative URLs
 		}
 
 		$local_url =~ s{//$}{/};
diff --git a/t/relativity.t b/t/relativity.t
index 6c4d1107e..675efc903 100755
--- a/t/relativity.t
+++ b/t/relativity.t
@@ -407,12 +407,9 @@ run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
 	$ENV{HTTPS} = 'on';
 });
 %bits = parse_cgi_content($content);
-TODO: {
-local $TODO = "avoid mixed content";
 is($bits{basehref}, "https://example.com/wiki/");
 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
 like($bits{tophref}, qr{^(?:/wiki|\.)/$});
-}
 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
 
 # when not accessed via HTTPS, ???
@@ -439,11 +436,13 @@ run(["./t/tmp/ikiwiki.cgi"], \undef, \$content, init => sub {
 	$ENV{HTTPS} = 'on';
 });
 %bits = parse_cgi_content($content);
+# because the static and dynamic stuff is on the same server, we assume that
+# both are also on the staging server
+like($bits{basehref}, qr{^https://staging.example.net/wiki/$});
+like($bits{stylehref}, qr{^(?:(?:https:)?//staging.example.net)?/wiki/style.css$});
+like($bits{tophref}, qr{^(?:(?:(?:https:)?//staging.example.net)?/wiki|\.)/$});
 TODO: {
-local $TODO = "avoid mixed content";
-like($bits{basehref}, qr{^https://example.com/wiki/$});
-like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
-like($bits{tophref}, qr{^(?:(?:(?:https:)?//example.com)?/wiki|\.)/$});
+local $TODO = "this should really point back to itself but currently points to example.com";
 like($bits{cgihref}, qr{^(?:(?:https:)?//staging.example.net)?/cgi-bin/ikiwiki.cgi$});
 }
 
@@ -458,11 +457,8 @@ run(["./t/tmp/ikiwiki.cgi"], \$in, \$content, init => sub {
 	$ENV{HTTPS} = 'on';
 });
 %bits = parse_cgi_content($content);
-TODO: {
-local $TODO = "avoid mixed content";
 is($bits{basehref}, "https://example.com/wiki/a/b/c/");
 like($bits{stylehref}, qr{^(?:(?:https:)?//example.com)?/wiki/style.css$});
-}
 like($bits{tophref}, qr{^(?:/wiki|\.\./\.\./\.\.)/$});
 like($bits{cgihref}, qr{^(?:(?:https:)?//example.com)?/cgi-bin/ikiwiki.cgi$});
 
diff --git a/t/urlto.t b/t/urlto.t
index 025409b7f..50cad88dd 100755
--- a/t/urlto.t
+++ b/t/urlto.t
@@ -50,11 +50,13 @@ is(IkiWiki::baseurl(undef), "//example.co.uk/~smcv/");
 is(IkiWiki::urlto('stoats', undef), "//example.co.uk/~smcv/stoats/");
 is(IkiWiki::urlto('', undef), "//example.co.uk/~smcv/");
 
-# with url and cgiurl on different schemes, "local" degrades to absolute
+# with url and cgiurl on different schemes, "local" degrades to absolute for
+# CGI but protocol-relative for static content, to avoid the CGI having
+# mixed content
 $IkiWiki::config{url} = "http://example.co.uk/~smcv";
 $IkiWiki::config{cgiurl} = "https://dynamic.example.co.uk/~smcv/ikiwiki.cgi";
 is(IkiWiki::checkconfig(), 1);
 is(IkiWiki::cgiurl(), "https://dynamic.example.co.uk/~smcv/ikiwiki.cgi");
-is(IkiWiki::baseurl(undef), "http://example.co.uk/~smcv/");
-is(IkiWiki::urlto('stoats', undef), "http://example.co.uk/~smcv/stoats/");
-is(IkiWiki::urlto('', undef), "http://example.co.uk/~smcv/");
+is(IkiWiki::baseurl(undef), "//example.co.uk/~smcv/");
+is(IkiWiki::urlto('stoats', undef), "//example.co.uk/~smcv/stoats/");
+is(IkiWiki::urlto('', undef), "//example.co.uk/~smcv/");
-- 
2.39.5