tags:

views:

349

answers:

2

I am using webmin and I am trying to change some settings in a file. I am having problems if the person uses any weird characters that might trip up sed or Perl using the following code:

&execute_command("sed -i 's/^$Pref.*\$/$Pref \"$in{$Pref}\"/g' $DIR/pserver.prefs.cache");

Where execute_command is a webmin function to basically run a special system call. $pref is the preference name such as "SERVERNAME", "OPTION2", etc. and $in{Pref} is going to be the option I want set for the PREF. For example here is a typical pserver.prefs:

SERVERNAME "Test Name"
OWNERPASSWORD "Hd8sdH&3"

Therefore, if we wanted to change SERVERNAME to say Tes"t#&^"@'"@@& and OWNERPASSWORD to *@(&'"@$"(')29 then they would be passed in as $in{Pref}. What is the easiest way to escape the $in{} varibles so that they can work OK with sed, or better yet, what is a way I can convert my sed command to a strictly Perl command so that it doesn't have errors?

Update:

Awesome, now I just trying to get it to work with '
I get this error:
/bin/sh: -c: line 0: unexpected EOF while looking >for matching `"' /bin/sh: -c: line 1: syntax error: unexpected end of file

This does not work:

my $Pref = "&*())(*&'''''^%$#@!"; 
&execute_command("perl -pi -e 's/^SERVERNAME.*\$/SERVERNAME \"\Q$Pref\E\"/g' $DIR/pserver.prefs");

This does:

my $Pref = "&*())(*&^%$#@!"; 
&execute_command("perl -pi -e 's/^SERVERNAME.*\$/SERVERNAME \"\Q$Pref\E\"/g' $DIR/pserver.prefs");
+4  A: 

Perl's regex support includes the \Q and \E operators, which will cause it to avoid interpreting regex symbols within their scope, yet they allow variable interpolation.

This works:

$i = '(*&%)*$£(*';

if ($i =~ /\Q$i\E/){
    print "matches!\n";
}

Without the \Q and \E, you'd get an error because of the regex symbols in $i.

Jeremy Smyth
A: 

The most trivial part is simply to stop executing a command as a single string. Get the shell out of it. Assuming your execute_command function just calls system under the covers, try:

execute_command(qw/perl -pi -e/, 's/^SERVERNAME.*$/SERVERNAME "\Q$Pref\E"/g', "$DIR/pserver.prefs");

That's better, but not perfect. After all, the user could put in something silly like "@[system qw:rm -rf /:]" and then silly things would happen. I think there are ways around this, too, but the most trivial might be to simply do the work inside your code. How to do that? Maybe starting with what perl is doing with the "-pi" flags might help. Let's take a peek:

$  perl -MO=Deparse -pi -e 's/^SERVERNAME.*$/SERVERNAME "\Qfoo\E"/'
BEGIN { $^I = ""; }
LINE: while (defined($_ = <ARGV>)) {
    s/^SERVERNAME.*$/SERVERNAME "foo"/;
}
continue {
    print $_;
}

Maybe you can do the same thing in your code? Not sure how easy that is to replicate, especially that $^I bit. Worst case scenario, read the file, write to a new file, delete the original file, rename the new file to the original name. That'll help get rid of all the exposures of passing dangerous junk around.

Tanktalus