-2

I am able to execute the below script on command line. echo "56.8 + 77.7" | bc -l

and I get proper output as well but when I try to do the same in my perl/html script and run it in browser it fails. Can you please guide me what's going wrong.

#!/opt/local/bin/perl

$res = 'echo "56.8 + 77.7" | bc -l';
print "Content-Type: text/html\n\n";
print "<html><body>\n";
print "<b>Calculator:</b><br/>\n";
print "<form action=\"/cgi-bin/calc.cgi\" methor=\"GET\">\n";
print "<table border=\"0\">\n";
print "<tr><td align=\"right\">Expression:</td>\n";
print "<td><input type=\"text\" name=\"exp\"";
print   " value=\"$exp\"/></td></tr>\n";
print "<tr><td align=\"right\">Result:</td>\n";
print "<td>$res</td></tr>\n";
print "<tr><td></td>\n";
print "<td><input type=\"submit\" value=\"Evaluate\"/></td></tr>\n";
print "</table></form>\n";
print "</html></body>\n";
exit;  

I tried using system as well but that didn't seem to work.

Sumit Raj
  • 113
  • 1
  • 3
  • 10
  • 5
    You want backticks or `qx//`. But... you know that Perl can do math, right? – Matt Jacob Nov 09 '15 at 05:38
  • Thanks a lot Matt, that's what I needed. Worked – Sumit Raj Nov 09 '15 at 05:48
  • 2
    `bc` is a shell command, not a Perl command. A much better approach would be to use Perl's built-in facilities, like `print "", 56.8+77.7, "\n";` – tripleee Nov 09 '15 at 06:19
  • 2
    @tripleee Ah, but this is a CGI calculator. Strap yourselves in, guys, we're going to pass GET parameters to the shell! – melpomene Nov 09 '15 at 08:27
  • @melpomene Rather than post rhetoric, it would be helpful if you explained to the OP why this is a really, really, really bad idea. – tripleee Nov 09 '15 at 08:29

3 Answers3

4

OK, so bc is a unix utility that lets you do maths. You can execute utility commands via the qx operator, or backticks.

However, in this case ... it's a bad idea, because it's a) unnecessary and b) presents a security risk. Why? Because you're passing user input from a web form, into a shell, which leaves you with a selection of injection exploits.

Why not instead:

my $res = 56.8 + 77.7;

Whilst you're at it, you really should:

  • turn on use strict;
  • turn on use warnings;
  • turn on "taint" mode. (add a -T to your shebang line).

This will reduce your chance of 'bad stuff' happening via code injection. When you switch on 'taint' mode, it marks any user input at 'tainted' and will not let you use it for certain commands without sanitising it first.

As an example - imagine if someone entered an expression:

4"; rm -rf /; echo "owned 

Your backticks to evaluate it would then become;

echo "4";rm -rf /; echo "owned" | bc -l

I'd imagine you can see how that's going to be bad news!

So you really shouldn't -ever- pass through user input to execute like that. You should also be quite cautious with eval. Using a regular expression to ensure your operation is 'just' mathematical in nature:

 unless ( $str =~ /[^0-9+-*^/.,_()]/ ) {
     eval ( $str );
 }

(Modules may also exist for this purpose)

Or:

my $eval_str = "5 + bogus; nonsense; here 210";

$eval_str =~ s{[^0-9\+\-\/\=\/]+}{}g;
print $eval_str;
Sobrique
  • 52,974
  • 7
  • 60
  • 101
  • Completely backwards. Using `bc` is far more safe than using `Perl`. Re "As an example - imagine if someone entered an expression:", That's due to a buggy Perl program, not the use of `bc`. – ikegami Nov 09 '15 at 12:38
0

As others have pointed out, you can do your calculations in Perl. There's no need to call bc.

But I wanted to look a little closer at the way you're producing your output. You're using a mass of print() statements. And there are far better approaches.

Firstly, you have statements that look like this:

print "<table border=\"0\">\n";

I know why you have the backslashes there, but doesn't it look ugly to you? You need to escape double-quote characters in double-quoted strings for obvious reasons. But Perl has the qq[...] operator for cases like this:

print qq[<table border="0">\n];

qq[...] works just like a double-quoted string but because it doesn't use double-quote characters to delimit the string you don't need to escape the double-quote characters inside the string. Note that you don't even need to use [...] to delimit the string. You can use almost any character - qq(...), qq|...| and many other options work just as well.

Secondly, why have a separate print() statement for each line. Whu not combine them into a single print() statement?

print qq[Content-Type: text/html

<html><body>
<b>Calculator:</b><br/>
<form action="/cgi-bin/calc.cgi" method="GET">
<table border="0">
<tr><td align="right">Expression:</td>
<td><input type="text" name="exp"
 value="$exp"/></td></tr>
<tr><td align="right">Result:</td>
<td>$res</td></tr>
<tr><td></td>
<td><input type="submit" value="Evaluate"/></td></tr>
</table></form>
</html></body>];

Notice how you can lose all the explicit \n and replace them with real newlines embedded in the string. Doesn't that look neater?

In fact most Perl programmers would take that a step further and use a "heredoc" for this:

print <<"ENDHTML";
Content-Type: text/html

<html><body>
<b>Calculator:</b><br/>
<form action="/cgi-bin/calc.cgi" method="GET">
<table border="0">
<tr><td align="right">Expression:</td>
<td><input type="text" name="exp"
 value="$exp"/></td></tr>
<tr><td align="right">Result:</td>
<td>$res</td></tr>
<tr><td></td>
<td><input type="submit" value="Evaluate"/></td></tr>
</table></form>
</html></body>
ENDHTML

Also, please consider using the CGI module's header() method to create your Content-Type header.

And finally, you might look at CGI::Alternatives to learn (among other things) how to move all of your HTML out into a separate file.

Dave Cross
  • 68,119
  • 3
  • 51
  • 97
0

IPC::Run3 and IPC::Run make it easy to both provide input to and collect output from a child process.

use IPC::Run3 qw( run3 );

my $equation = '56.8 + 77.7';

run3([ 'bc', '-l' ], $equation, \my $stdout, \my $stderr);

The benefits of this approach:

  • No shell is involved which avoids the possibility of accidental shell-code injection.

  • Perl's eval isn't involved which avoids the possibility of accidental Perl-code injection.

  • No need to write your own equation parser.

ikegami
  • 367,544
  • 15
  • 269
  • 518