in reply to RESOLVED: Slurping search-replace patterns from a file

use strict; use warnings; while (<DATA>) { chomp; my ($pat, $repl) = (split '/') [1, 2]; my $s = "foo"; $s =~ s/$pat/qq{"$repl"}/ee; say $s; } __DATA__ s/foo/bar/ s/(fo)o/bar$1/
This prints:
bar barfo

Replies are listed 'Best First'.
Re^2: Slurping search-replace patterns from a file
by moritz (Cardinal) on Oct 14, 2008 at 15:40 UTC
    Please don't recommend something like that without mentioning that the /ee modifier calls eval and thus opens the door for execution of arbitrary code.

    A safe alternative might be String::Interpolate, but it might require a bit more work.

    Update: if it's not obvious what the unsafe part is, look at this:

    my $x = 'abc'; my $evil = '"; print "evil stuff here\n"; "'; $x =~s/a(bc)/qq{"$evil"}/ee;

    Instead of the print... arbitrary perl code can be executed.

      Since the issue is the interpolation of the replacement string, can you not stamp on '$' and '@', leaving only '$1' et al ? That is:

      use strict ; use warnings ; while (<DATA>) { my ($pat, $repl) = (split '/') [1, 2]; # Escape $ and @ preceded by an even number of '\', except for $1 +etc. $repl =~ s/(?<!\\)((?:\\\\)*(?:\$(?!\d)|\@))/\\$1/g ; my $e = "\$s =~ s/$pat/$repl/" ; my $s = "foo"; print "$e '$s' -> " ; eval $e ; die $@ if $@ ; print "'$s'\n" ; } __DATA__ s/foo/bar/ s/(fo)o/bar$1/ s/oo/\${system "echo hello sailor !"}/ s/oo/\\${system "echo hello sailor !"}/ s/oo/\\\${system "echo hello sailor !"}/ s/oo/\\\\${system "echo hello sailor !"}/ s/oo/\\\\\${system "echo hello sailor !"}/ s/oo/@{system "echo hello sailor !"}/ s/oo/\@{system "echo hello sailor !"}/ s/oo/\\@{system "echo hello sailor !"}/
      Noting that you don't want to "activate" '\$' or '\@' by inserting '\'.

      Hope I didn't miss anything...

        Hope I didn't miss anything...

        That's the crucial point: You never know. Perl is a language with a very complex syntax (compared to all other programming languages that I know of) that you can basically never be sure if you approach things in this way.

        I thought I was quite confident with Perl's syntax, until I found out that you can have whitespaces between the sigil and the variable, and that even works in string interpolation:

        $ perl -wle 'my $x = 3; print "x: $ x"' x: 3

        I certainly didn't expect that.

        What I learned from this is to never assume that you know every detail of Perl syntax, so I won't be sure that I've covered all corner cases. Can you be?

Re^2: Slurping search-replace patterns from a file
by oko1 (Deacon) on Oct 14, 2008 at 15:40 UTC

    Unfortunately, this is rather fragile - if "$repl" contains, say, a double quote, it breaks. My solution is somewhat similar, but it uses an actual "eval" so I can catch/warn about the errors:

    #!/usr/bin/perl -w use strict; while (<DATA>) { next unless /^\s*s\//; # Minimal checking chomp; my ($pat, $repl) = (split '/') [1, 2]; my $s = "foo"; eval "\$s =~ s/$pat/$repl/"; warn "Warning: $@" if $@; print "$s\n"; } __DATA__ s/foo/bar/ s/(fo)o/bar$1/

    --
    "Language shapes the way we think, and determines what we can think about."
    -- B. L. Whorf
      ... and is just as vulnerable to code injection as the original solution. Add this line to the __DATA__ section:
      s/./${system "echo foo"}/
      Or instead of echo foo you can write rm -rf ~/* - I think you get the idea pretty quickly.

        Thanks for the feedback. The eval statement works (thanks for that), but as Moritz mentioned, it's very vulnerable. I don't expect this file to be exposed to malicious users, but it's still something I'd like to mitigate, if possible.

        I'm going through the documentation of String::Interpolate, and we'll see if that meets my needs.

        Thanks again for all the help.

        -HKS