In trying to run some code with taint checking, I found the following snippet:
local $/; my %conf = eval <CONF>;
Needless to say, that fails in taint mode. I had three choices. I could either spend two or three days ripping out all of the configuration code which writes to that file and replace it with a standard configuration set up, find a module on the CPAN which implements this or I could reinvent the wheel.
As you can see from the code, whatever is in CONF should eval to hash. The only thing I found which does that directly was Parse::PerlConfig. Unfortunately, that uses eval. Given the sheer number of config files to wade through, I suspect there might be something which handles this properly, but I couldn't find it. The config file is in a simple format like this:
#!/usr/bin/perl # Which store are we using? store => { class => 'Store::DB::SQLite', }, # Configuration for the PostgreSQL data store. # pg => { # } # Where is the data store? sqlite => { file => 't/data/store.db', },
Basically, we have a hash of hashes. Each contained hash is guaranteed to be a simple list of key/value pairs. As a quick hack, I through together this:
use Regexp::Common; { my $shebang_re = qr/#!\S*/; my $bareword_re = qr/[[:word:]]+/; my $quoted_re = $RE{quoted}; my $comma_re = qr/(?:=>|,)/; my $n_re = qr/\s*(?:\n|\r)?\s*/; my $pair_re = qr/\s*$bareword_re\s*$comma_re\s*(?:$bareword_re|$qu +oted_re)/; my $hash_body_re = qr/\s*{\s*(?:$pair_re\s*$comma_re\s*)*\s*(?:$pair_re\s*$comma_re +?\s*)\s*}\s*/; my $comment_re = qr/(?:^\s*#.*$n_re)*/m; my $hash_re = qr/\s*$comment_re?\s*$bareword_re\s*$comma_re\s*$hash_body_re\s* +/; my $hashes_re = qr/\s*(?:$hash_re\s*$comma_re)*\s*(?:$hash_re\s*$comma_re?\s*)/; my $conf_re = qr/$shebang_re?\s*$hashes_re\s*/; sub _untaint_config { my $_conf = shift; my ($conf) = $_conf =~ /^($conf_re)$/sm; return $conf; } # testing hooks if ( $ENV{HARNESS_ACTIVE} ) { *_comma_re = sub { $comma_re }; *_comment_re = sub { $comment_re }; *_pair_re = sub { $pair_re }; *_hash_body_re = sub { $hash_body_re }; *_conf_re = sub { $conf_re }; } }
That's phenomenally ugly, but it works. It lets me do this:
local $/; my %conf = eval _untaint_config(<CONF>);
It also tightly restricts the use of eval and since this is deliberately Perl-like without actually being Perl, I can't see how someone would accidentally slip in naughty data. The problem is, that is so ugly that I don't want it in our code and I would be much happier if there's already a module out there which would handle this more gracefully. Further, while it works and passes my tests, I'm sure it has bugs. I want: no eval and a very restricted syntax. Is that out there?
Side note: yeah, I should probably convert that to Regexp::Assemble but it's surprisingly fast.
Cheers,
Ovid
New address of my CGI Course.
In reply to "eval"ing a hash without eval by Ovid
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |