Skip to content

Commit

Permalink
Implmented verify_checksums plugin.
Browse files Browse the repository at this point in the history
  • Loading branch information
miyagawa committed Feb 28, 2010
1 parent 2035cef commit 5da1634
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
10 changes: 10 additions & 0 deletions lib/App/cpanminus/script.pm
Expand Up @@ -682,6 +682,16 @@ sub fetch_module {
}

$self->diag("OK\n");

# TODO add more metadata so plugins can tell how to verify and pass through
my $args = { file => $file, uri => $uri, fail => 0 };
$self->run_hooks(verify_archive => $args);

if ($args->{fail} && !$self->{force}) {
$self->diag("! Verifying the archive $file failed. Skipping. (use --force to install)\n");
next;
}

$self->chat("Unpacking $file\n");

my $dir = $file =~ /\.zip/i ? $self->unzip($file) : $self->untar($file);
Expand Down
75 changes: 75 additions & 0 deletions plugins/verify_checksums
@@ -0,0 +1,75 @@
name 'verify_checksums';
description 'Verify CHECKSUMS before unpacking the archive';
author 'Tatsuhiko Miyagawa';

api_version 0.1;

hook verify_archive => sub {
my $args = shift;

# Just require and throw errors -- cpanminus will catch them and display it in the build.log
require Module::Signature;
require Digest::SHA;

# TODO move this logic to cpanminus and set 'source' parameter
my $base = $args->{uri};
$base =~ s!(authors/id/[A-Z]/[A-Z]{2}/([A-Z]+))/[^/]+\.(?:tar\.gz|tgz|zip)$!$1!
or return;

my $pause_id = $2; # TODO this can be in the context
my $chk_uri = "$base/CHECKSUMS";
$args->{app}->chat("Fetching $chk_uri to verifying checksums...\n");

my $chk_file = "$pause_id.CHECKSUMS";
$args->{app}->mirror($chk_uri, $chk_file);

if (-e $chk_file) {
$args->{app}->chat("Verifying the signature of CHECKSUMS itself...\n");

my $rv = eval {
my $v = Module::Signature::_verify($chk_file);
$v == Module::Signature::SIGNATURE_OK();
};
if ($rv) {
$args->{app}->chat("Verified OK!\n");
} else {
$args->{app}->diag("Verifying CHECKSUMS signature failed: $rv\n");
return $args->{fail}++;
}

$args->{app}->chat("Verifying the SHA1 for $args->{file}\n");

open my $fh, "<$chk_file" or die "$chk_file: $!";
my $data = join '', <$fh>;
$data =~ s/\015?\012/\n/g;

require Safe;
my $chksum = Safe->new->reval($data);

if (!ref $chksum or ref $chksum ne 'HASH') {
$args->{app}->diag("! Checksum file downloaded from $chk_file is broken.\n");
return $args->{fail}++;
}

if (my $sha = $chksum->{$args->{file}}{sha256}) {
$args->{app}->chat("Found the checksum for $args->{file}: $sha\n");

open my $fh, "<$args->{file}" or die "$args->{file}: $!";
my $dg = Digest::SHA->new(256);
my($data);
while (read($fh, $data, 4096)) {
$dg->add($data);
}

my $hex = $dg->hexdigest;
if ($hex eq $sha) {
$args->{app}->chat("Checksum for $args->{file}: Verified!\n");
} else {
$args->{app}->diag("! Checksum mismatch for $args->{file}: $hex != $sha\n");
}
} else {
$args->{app}->chat("Checksum for $args->{file} not found in CHECKSUMS.\n");
}

}
};

0 comments on commit 5da1634

Please sign in to comment.