From 23a4ee6d15dbd9b8e8c6588a829dd30a26a8de32 Mon Sep 17 00:00:00 2001
From: Joey Hess <joey@gnu.kitenet.net>
Date: Mon, 18 May 2009 15:25:10 -0400
Subject: [PATCH] Allow curly braces to be used in pagespecs

And avoid a whole class of potential security problems (though
none that I know of actually existing..), by avoiding
performing any string interpolation on user-supplied data when translating
pagespecs.
---
 IkiWiki.pm                                       | 16 +++++++---------
 debian/changelog                                 |  3 +++
 ...__39__t_match___123__curly__125___braces.mdwn |  2 +-
 t/pagespec_match.t                               |  4 +++-
 4 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/IkiWiki.pm b/IkiWiki.pm
index 6233d2ead..061a1c6db 100644
--- a/IkiWiki.pm
+++ b/IkiWiki.pm
@@ -1678,12 +1678,6 @@ sub rcs_receive () {
 	$hooks{rcs}{rcs_receive}{call}->();
 }
 
-sub safequote ($) {
-	my $s=shift;
-	$s=~s/[{}]//g;
-	return "q{$s}";
-}
-
 sub add_depends ($$) {
 	my $page=shift;
 	my $pagespec=shift;
@@ -1785,6 +1779,7 @@ sub pagespec_translate ($) {
 
 	# Convert spec to perl code.
 	my $code="";
+	my @data;
 	while ($spec=~m{
 		\s*		# ignore whitespace
 		(		# 1: match a single word
@@ -1812,14 +1807,17 @@ sub pagespec_translate ($) {
 		}
 		elsif ($word =~ /^(\w+)\((.*)\)$/) {
 			if (exists $IkiWiki::PageSpec::{"match_$1"}) {
-				$code.="IkiWiki::PageSpec::match_$1(\$page, ".safequote($2).", \@_)";
+				push @data, $2;
+				$code.="IkiWiki::PageSpec::match_$1(\$page, \$data[$#data], \@_)";
 			}
 			else {
-				$code.="IkiWiki::ErrorReason->new(".safequote(qq{unknown function in pagespec "$word"}).")";
+				push @data, qq{unknown function in pagespec "$word"};
+				$code.="IkiWiki::ErrorReason->new(\$data[$#data])";
 			}
 		}
 		else {
-			$code.=" IkiWiki::PageSpec::match_glob(\$page, ".safequote($word).", \@_)";
+			push @data, $word;
+			$code.=" IkiWiki::PageSpec::match_glob(\$page, \$data[$#data], \@_)";
 		}
 	}
 
diff --git a/debian/changelog b/debian/changelog
index c2819d0c5..7efa31cf1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -6,6 +6,9 @@ ikiwiki (3.13) UNRELEASED; urgency=low
     of other underlays via add_underlay.
   * More friendly display of markdown, textile in edit form selector
     (jmtd)
+  * Allow curly braces to be used in pagespecs, and avoid a whole class
+    of potential security problems, by avoiding performing any string
+    interpolation on user-supplied data when translating pagespecs.
 
  -- Joey Hess <joeyh@debian.org>  Wed, 06 May 2009 20:45:44 -0400
 
diff --git a/doc/bugs/pagespec_can__39__t_match___123__curly__125___braces.mdwn b/doc/bugs/pagespec_can__39__t_match___123__curly__125___braces.mdwn
index c03f82907..e3146d92a 100644
--- a/doc/bugs/pagespec_can__39__t_match___123__curly__125___braces.mdwn
+++ b/doc/bugs/pagespec_can__39__t_match___123__curly__125___braces.mdwn
@@ -35,6 +35,6 @@ More tests:
 > * Avoid exposing user input to interpolation as a string. One
 >   way that comes to mind is to have a local string lookup hash,
 >   and insert each user specified string into it, then use the hash
->   to lookup the specified strings at runtime.
+>   to lookup the specified strings at runtime. [[done]]
 > 
 > --[[Joey]] 
diff --git a/t/pagespec_match.t b/t/pagespec_match.t
index 69cf361de..4cf6fa1ff 100755
--- a/t/pagespec_match.t
+++ b/t/pagespec_match.t
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use warnings;
 use strict;
-use Test::More tests => 51;
+use Test::More tests => 53;
 
 BEGIN { use_ok("IkiWiki"); }
 
@@ -28,6 +28,8 @@ ok(pagespec_match("a/foo", "./*", "a/b"), "relative oldstyle call");
 ok(pagespec_match("foo", "./*", location => "a"), "relative toplevel");
 ok(pagespec_match("foo/bar", "*", location => "baz"), "absolute");
 ok(! pagespec_match("foo", "foo and bar"), "foo and bar");
+ok(pagespec_match("{f}oo", "{*}*"), "curly match");
+ok(! pagespec_match("foo", "{*}*"), "curly !match");
 
 # The link and backlink stuff needs this.
 $config{userdir}="";
-- 
2.39.5