Most code which attempts to find the version number of a given module either uses a naive regex, uses "eval" on the version line, or loads the module and calls $module->VERSION.
I'm writing Module::SafeVersion which attempts to do this correctly, but it may never get released. I'm employing a combination approach of using a rather complicated regex along with some "cleanup" code to handle the majority of cases. Currently it properly extracts about 98% of the versions of modules installed on my laptop. However, the special cases are significant and I can't ignore them.
I can try to continually expand the regex and the cleanup code, but I'm sadly beginning to think that what needs to be done is to build in special cases for those modules which have jumped through ridiculous hoops to generate their version numbers. Let's look at some examples:
# From SOAP::Lite
$VERSION = sprintf("%d.%s", map {s/_//g; $_} q$Name: release-0_60-public $ =~ /-(\d+)_([\d_]+)/)
# From SQL::Abstract
$VERSION = do { my @r=(q$Revision: 1.20 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
# From CPAN
my $VERSION = sprintf "%.2f", substr(q$Rev: 245 $,4)/100;
Schwern argued that this entire module is the wrong approach. Specifically, if eval'ing a version string is a problem, this implies that you already have bad code on your box and the version string is the least of your problems. PAUSE apparently runs in a chroot jail, so the only folks likely to be bitten by a version string attack would be smoke testers. Thus, if I have code on my box, it should automatically be viewed as trusted. Thoughts?
eval
bart on 2006-04-25T10:42:13
I'm not sure if this is indeed the wrong approach. I agree that once you have a module installed on your system, it should be safe to eval. (Was it safe to install anyway? How can you be sure about that, about any module on CPAN?)
What I do have doubts about is whether you can be sure that this one line is all you need. Perhaps some author spread the statement over two lines? Surely that is valid in Perl.
Yes, loading the module and calling
$module->VERSION
(or checking
${"$module\::VERSION"}
, with
strict
off) works. But if I have any interest in a module for doing this work, I'd prefer one that doesn't keep a module loaded in memory afterwards. And for that reason alone, I might still prefer
eval
.
Or I might just stick to a system call to an external program, as in
http://perlmonks.org/?node_id=321286. It's not that complicated
:).
I think it's worth it
Alias on 2006-04-25T12:05:31
The problem of course is that of containment.
While it is alright for people to SAY that "It's installed so you have to trust it" that's not going to be enough to satisfy tainting. Tainting quite rightfully doesn't care WHY, it just enforces the rules.
Look at other situations.
Imagine you have a module open in an editor, and the editor wants to intuit the version of the code. The only option available is to apply the same unsafe execution methods.
This, of course, is rediculous, because as (I hope) I've already established, anything like this can be a vector for attacking your system.
Today, it's trivial to launch a Denial of Service attack on PAUSE regardless of the jails. I could also fairly trivially destroy people's home directories, and I can see a number of different problematic vectors.
If nothing else, your module gives us a fallback for situations in which it is important that we care about security, and secondarily it to a degree it encourages the use of good behaviours in versioning instead of (arguably) bad behaviours like repository-specific versioning.
A lot of the value in technologies like CVS tags and XML Schemas, and indeed PPI as well, are that it allows you to work in environments EVEN IF you don't or can't trust the environment.
That places it in the same category as tainting itself, or a position compared to the MakeMaker version extraction that PPI is in compared to Perl itself.
Just beware that this is an Open Problem, you will never be able to solve it 100%. So solve it as much as you reasonably can, allow other people to add improvements if they need it, and then the mere fact yours works under tainting will in and of itself encourage better behaviour from module writers, just as PPI encourages people not to use the most extreme forms of Perl syntax abuse.
I say go for it. It's better we have it than not have it.
Undecided
Aristotle on 2006-04-26T09:00:33
I am quite sympathetic to Schwern’s point of view and was actually thinking of arguing the same.
But I suppose there’s value in solving this, anyway.
Question: would it not work better to eval
the code in a Safe
compartment with all opcodes disabled save for assignment, subst/matching, and a few popular other ones?
Also: that name is horrible. I thought it was going to have something to do with picking a safe version of a module. You want something like Module::ExtractVersion::Safely, maybe.