Detect group matches in replacements more robustly

The Python re module uses '\n' for group backreference substitution,
whereas mod_alias uses '$n', and thus we must translate between them
when processing RedirectMatch directives. Previously this was done by
substituting backslashes for any dollar signs in the replacement string.
However, group substitution only occurs when the $ is not escaped with a
backslash and is followed by a decimal digit. Follow the same rules
here.

Change-Id: Ib33534b40e0b126c769ec17f76e1ecc83d790ebc
This commit is contained in:
Zane Bitter 2017-11-08 16:45:24 -05:00
parent 7e37a20c79
commit 006ffc075c
3 changed files with 29 additions and 1 deletions

View File

@ -0,0 +1,3 @@
fixes:
- Literal '$' characters are now handled correctly when they appear in
substitution strings for RedirectMatch directives.

View File

@ -70,14 +70,19 @@ class Redirect(Rule):
class RedirectMatch(Rule):
"A RedirectMatch rule with a regular expression."
_group_subst = re.compile(r'(?<!\\)\$([0-9])')
def __init__(self, linenum, *params):
super(RedirectMatch, self).__init__(linenum, *params)
self.regex = re.compile(self.pattern)
if self.target:
self.target_repl = self.target.replace('$', '\\')
self.target_repl = self._get_target_repl()
else:
self.target_repl = None
def _get_target_repl(self):
return self._group_subst.sub(r'\\1', self.target).replace(r'\$', '$')
def match(self, path):
m = self.regex.search(path)
if m:

View File

@ -106,6 +106,26 @@ class TestRedirectMatch(base.TestCase):
rule.match('/user/foo'),
)
def test_match_with_no_group_dollar(self):
rule = rules.RedirectMatch(
1,
'redirectmatch', '301', '^/user/(.*)$', '/pike/user/$a',
)
self.assertEqual(
('301', '/pike/user/$a'),
rule.match('/user/foo'),
)
def test_match_with_group_escape(self):
rule = rules.RedirectMatch(
1,
'redirectmatch', '301', '^/user/(.*)$', '/pike/user/\\$1',
)
self.assertEqual(
('301', '/pike/user/$1'),
rule.match('/user/foo'),
)
def test_no_match(self):
rule = rules.RedirectMatch(
1,