P
Peter J. Holzer
I want to provide non-trusted users with a way to make substitutions
similar to s///. To my surprise this isn't an FAQ (although there is a
similar problem - "How can I expand variables in text strings?"), but
maybe I shouldn't be surprised because this is the first time I needed
to do that myself ;-).
So here is what I came up with - please try to shoot it down:
sub replace {
my ($string, $pattern, $replacement) = @_;
if (my @m = $string =~ m/$pattern/p) {
for my $i (1 .. @m) {
$replacement =~ s/\$$i(?=\D|)/$m[$i-1]/e;
}
return ${^PREMATCH} . $replacement . ${^POSTMATCH};
} else {
return $string;
}
}
All three strings are potentially untrusted ($string comes from a
database query, $pattern and $replacement are supplied by the user.
The function should do the same as
$string =~ s/$pattern/$replacement/;
except that $<number> references to capture buffers are resolved, too.
I think this is safe:
* The match itself should be safe unless "use re 'eval'" is active
(it isn't).
* The s///e only evaluates to $m[$i-1], it doesn't evaluate the
content, so it cannot be used to inject code.
There is one catch, though: If $pattern doesn't contain any capturing
parentheses, @m is set to (1) on a successful match, which isn't
distinguishable from a pattern which captures one string "1". I guess I
could try to analyze $pattern or just document that at least one set of
parentheses must be used.
Did I miss something? Is there a simpler way?
hp
similar to s///. To my surprise this isn't an FAQ (although there is a
similar problem - "How can I expand variables in text strings?"), but
maybe I shouldn't be surprised because this is the first time I needed
to do that myself ;-).
So here is what I came up with - please try to shoot it down:
sub replace {
my ($string, $pattern, $replacement) = @_;
if (my @m = $string =~ m/$pattern/p) {
for my $i (1 .. @m) {
$replacement =~ s/\$$i(?=\D|)/$m[$i-1]/e;
}
return ${^PREMATCH} . $replacement . ${^POSTMATCH};
} else {
return $string;
}
}
All three strings are potentially untrusted ($string comes from a
database query, $pattern and $replacement are supplied by the user.
The function should do the same as
$string =~ s/$pattern/$replacement/;
except that $<number> references to capture buffers are resolved, too.
I think this is safe:
* The match itself should be safe unless "use re 'eval'" is active
(it isn't).
* The s///e only evaluates to $m[$i-1], it doesn't evaluate the
content, so it cannot be used to inject code.
There is one catch, though: If $pattern doesn't contain any capturing
parentheses, @m is set to (1) on a successful match, which isn't
distinguishable from a pattern which captures one string "1". I guess I
could try to analyze $pattern or just document that at least one set of
parentheses must be used.
Did I miss something? Is there a simpler way?
hp