|
Packit |
14c646 |
#!./perl
|
|
Packit |
14c646 |
#
|
|
Packit |
14c646 |
# Copyright (c) 2002 Slaven Rezic
|
|
Packit |
14c646 |
#
|
|
Packit |
14c646 |
# You may redistribute only under the same terms as Perl 5, as specified
|
|
Packit |
14c646 |
# in the README file that comes with the distribution.
|
|
Packit |
14c646 |
#
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
sub BEGIN {
|
|
Packit |
14c646 |
unshift @INC, 't';
|
|
Packit |
14c646 |
unshift @INC, 't/compat' if $] < 5.006002;
|
|
Packit |
14c646 |
require Config; import Config;
|
|
Packit |
14c646 |
if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) {
|
|
Packit |
14c646 |
print "1..0 # Skip: Storable was not built\n";
|
|
Packit |
14c646 |
exit 0;
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
use strict;
|
|
Packit |
14c646 |
BEGIN {
|
|
Packit |
14c646 |
if (!eval q{
|
|
Packit |
14c646 |
use Test::More;
|
|
Packit |
14c646 |
use B::Deparse 0.61;
|
|
Packit |
14c646 |
use 5.006;
|
|
Packit |
14c646 |
1;
|
|
Packit |
14c646 |
}) {
|
|
Packit |
14c646 |
print "1..0 # skip: tests only work with B::Deparse 0.61 and at least perl 5.6.0\n";
|
|
Packit |
14c646 |
exit;
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
require File::Spec;
|
|
Packit |
14c646 |
if ($File::Spec::VERSION < 0.8) {
|
|
Packit |
14c646 |
print "1..0 # Skip: newer File::Spec needed\n";
|
|
Packit |
14c646 |
exit 0;
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
BEGIN { plan tests => 63 }
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
use Storable qw(retrieve store nstore freeze nfreeze thaw dclone);
|
|
Packit |
14c646 |
use Safe;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
#$Storable::DEBUGME = 1;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
our ($freezed, $thawed, @obj, @res, $blessed_code);
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$blessed_code = bless sub { "blessed" }, "Some::Package";
|
|
Packit |
14c646 |
{ package Another::Package; sub foo { __PACKAGE__ } }
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
no strict; # to make the life for Safe->reval easier
|
|
Packit |
14c646 |
sub code { "JAPH" }
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
local *FOO;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
@obj =
|
|
Packit |
14c646 |
([\&code, # code reference
|
|
Packit |
14c646 |
sub { 6*7 },
|
|
Packit |
14c646 |
$blessed_code, # blessed code reference
|
|
Packit |
14c646 |
\&Another::Package::foo, # code in another package
|
|
Packit |
14c646 |
sub ($$;$) { 0 }, # prototypes
|
|
Packit |
14c646 |
sub { print "test\n" },
|
|
Packit |
14c646 |
\&Storable::_store, # large scalar
|
|
Packit |
14c646 |
],
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{"a" => sub { "srt" }, "b" => \&code},
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
sub { ord("a")-ord("7") },
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
\&code,
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
\&dclone, # XS function
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
sub { open FOO, '<', "/" },
|
|
Packit |
14c646 |
);
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$Storable::Deparse = 1;
|
|
Packit |
14c646 |
$Storable::Eval = 1;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
# Test freeze & thaw
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[0];
|
|
Packit |
14c646 |
$thawed = thaw $freezed;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
is($thawed->[0]->(), "JAPH");
|
|
Packit |
14c646 |
is($thawed->[1]->(), 42);
|
|
Packit |
14c646 |
is($thawed->[2]->(), "blessed");
|
|
Packit |
14c646 |
is($thawed->[3]->(), "Another::Package");
|
|
Packit |
14c646 |
is(prototype($thawed->[4]), prototype($obj[0]->[4]));
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[1];
|
|
Packit |
14c646 |
$thawed = thaw $freezed;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
is($thawed->{"a"}->(), "srt");
|
|
Packit |
14c646 |
is($thawed->{"b"}->(), "JAPH");
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[2];
|
|
Packit |
14c646 |
$thawed = thaw $freezed;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
is($thawed->(), (ord "A") == 193 ? -118 : 42);
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[3];
|
|
Packit |
14c646 |
$thawed = thaw $freezed;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
is($thawed->(), "JAPH");
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
eval { $freezed = freeze $obj[4] };
|
|
Packit |
14c646 |
like($@, qr/The result of B::Deparse::coderef2text was empty/);
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
# Test dclone
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
my $new_sub = dclone($obj[2]);
|
|
Packit |
14c646 |
is($new_sub->(), $obj[2]->());
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
# Test retrieve & store
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
store $obj[0], "store$$";
|
|
Packit |
14c646 |
# $Storable::DEBUGME = 1;
|
|
Packit |
14c646 |
$thawed = retrieve "store$$";
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
is($thawed->[0]->(), "JAPH");
|
|
Packit |
14c646 |
is($thawed->[1]->(), 42);
|
|
Packit |
14c646 |
is($thawed->[2]->(), "blessed");
|
|
Packit |
14c646 |
is($thawed->[3]->(), "Another::Package");
|
|
Packit |
14c646 |
is(prototype($thawed->[4]), prototype($obj[0]->[4]));
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
nstore $obj[0], "store$$";
|
|
Packit |
14c646 |
$thawed = retrieve "store$$";
|
|
Packit |
14c646 |
unlink "store$$";
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
is($thawed->[0]->(), "JAPH");
|
|
Packit |
14c646 |
is($thawed->[1]->(), 42);
|
|
Packit |
14c646 |
is($thawed->[2]->(), "blessed");
|
|
Packit |
14c646 |
is($thawed->[3]->(), "Another::Package");
|
|
Packit |
14c646 |
is(prototype($thawed->[4]), prototype($obj[0]->[4]));
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
######################################################################
|
|
Packit |
14c646 |
# Security with
|
|
Packit |
14c646 |
# $Storable::Eval
|
|
Packit |
14c646 |
# $Storable::Deparse
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
local $Storable::Eval = 0;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
for my $i (0 .. 1) {
|
|
Packit |
14c646 |
$freezed = freeze $obj[$i];
|
|
Packit |
14c646 |
$@ = "";
|
|
Packit |
14c646 |
eval { $thawed = thaw $freezed };
|
|
Packit |
14c646 |
like($@, qr/Can\'t eval/);
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
local $Storable::Deparse = 0;
|
|
Packit |
14c646 |
for my $i (0 .. 1) {
|
|
Packit |
14c646 |
$@ = "";
|
|
Packit |
14c646 |
eval { $freezed = freeze $obj[$i] };
|
|
Packit |
14c646 |
like($@, qr/Can\'t store CODE items/);
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
local $Storable::Eval = 0;
|
|
Packit |
14c646 |
local $Storable::forgive_me = 1;
|
|
Packit |
14c646 |
for my $i (0 .. 4) {
|
|
Packit |
14c646 |
$freezed = freeze $obj[0]->[$i];
|
|
Packit |
14c646 |
$@ = "";
|
|
Packit |
14c646 |
eval { $thawed = thaw $freezed };
|
|
Packit |
14c646 |
is($@, "");
|
|
Packit |
14c646 |
like($$thawed, qr/^sub/);
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
local $Storable::Deparse = 0;
|
|
Packit |
14c646 |
local $Storable::forgive_me = 1;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
my $devnull = File::Spec->devnull;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
open(SAVEERR, ">&STDERR");
|
|
Packit |
14c646 |
open(STDERR, '>', $devnull) or
|
|
Packit |
14c646 |
( print SAVEERR "Unable to redirect STDERR: $!\n" and exit(1) );
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
eval { $freezed = freeze $obj[0]->[0] };
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
open(STDERR, ">&SAVEERR");
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
is($@, "");
|
|
Packit |
14c646 |
isnt($freezed, '');
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
my $safe = new Safe;
|
|
Packit |
14c646 |
local $Storable::Eval = sub { $safe->reval(shift) };
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[0]->[0];
|
|
Packit |
14c646 |
$@ = "";
|
|
Packit |
14c646 |
eval { $thawed = thaw $freezed };
|
|
Packit |
14c646 |
is($@, "");
|
|
Packit |
14c646 |
is($thawed->(), "JAPH");
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[0]->[6];
|
|
Packit |
14c646 |
eval { $thawed = thaw $freezed };
|
|
Packit |
14c646 |
# The "Code sub ..." error message only appears if Log::Agent is installed
|
|
Packit |
14c646 |
like($@, qr/(trapped|Code sub)/);
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
if (0) {
|
|
Packit |
14c646 |
# Disable or fix this test if the internal representation of Storable
|
|
Packit |
14c646 |
# changes.
|
|
Packit |
14c646 |
skip("no malicious storable file check", 1);
|
|
Packit |
14c646 |
} else {
|
|
Packit |
14c646 |
# Construct malicious storable code
|
|
Packit |
14c646 |
$freezed = nfreeze $obj[0]->[0];
|
|
Packit |
14c646 |
my $bad_code = ';open FOO, "/badfile"';
|
|
Packit |
14c646 |
# 5th byte is (short) length of scalar
|
|
Packit |
14c646 |
my $len = ord(substr($freezed, 4, 1));
|
|
Packit |
14c646 |
substr($freezed, 4, 1, chr($len+length($bad_code)));
|
|
Packit |
14c646 |
substr($freezed, -1, 0, $bad_code);
|
|
Packit |
14c646 |
$@ = "";
|
|
Packit |
14c646 |
eval { $thawed = thaw $freezed };
|
|
Packit |
14c646 |
like($@, qr/(trapped|Code sub)/);
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
my $safe = new Safe;
|
|
Packit |
14c646 |
# because of opcodes used in "use strict":
|
|
Packit |
14c646 |
$safe->permit(qw(:default require caller));
|
|
Packit |
14c646 |
local $Storable::Eval = sub { $safe->reval(shift) };
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[0]->[1];
|
|
Packit |
14c646 |
$@ = "";
|
|
Packit |
14c646 |
eval { $thawed = thaw $freezed };
|
|
Packit |
14c646 |
is($@, "");
|
|
Packit |
14c646 |
is($thawed->(), 42);
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
package MySafe;
|
|
Packit |
14c646 |
sub new { bless {}, shift }
|
|
Packit |
14c646 |
sub reval {
|
|
Packit |
14c646 |
my $source = $_[1];
|
|
Packit |
14c646 |
# Here you can apply some nifty regexpes to ensure the
|
|
Packit |
14c646 |
# safeness of the source code.
|
|
Packit |
14c646 |
my $coderef = eval $source;
|
|
Packit |
14c646 |
$coderef;
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
my $safe = new MySafe;
|
|
Packit |
14c646 |
local $Storable::Eval = sub { $safe->reval($_[0]) };
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$freezed = freeze $obj[0];
|
|
Packit |
14c646 |
eval { $thawed = thaw $freezed };
|
|
Packit |
14c646 |
is($@, "");
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
if ($@ ne "") {
|
|
Packit |
14c646 |
fail() for (1..5);
|
|
Packit |
14c646 |
} else {
|
|
Packit |
14c646 |
is($thawed->[0]->(), "JAPH");
|
|
Packit |
14c646 |
is($thawed->[1]->(), 42);
|
|
Packit |
14c646 |
is($thawed->[2]->(), "blessed");
|
|
Packit |
14c646 |
is($thawed->[3]->(), "Another::Package");
|
|
Packit |
14c646 |
is(prototype($thawed->[4]), prototype($obj[0]->[4]));
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
# Check internal "seen" code
|
|
Packit |
14c646 |
my $short_sub = sub { "short sub" }; # for SX_SCALAR
|
|
Packit |
14c646 |
# for SX_LSCALAR
|
|
Packit |
14c646 |
my $long_sub_code = 'sub { "' . "x"x255 . '" }';
|
|
Packit |
14c646 |
my $long_sub = eval $long_sub_code; die $@ if $@;
|
|
Packit |
14c646 |
my $sclr = \1;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
local $Storable::Deparse = 1;
|
|
Packit |
14c646 |
local $Storable::Eval = 1;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
for my $sub ($short_sub, $long_sub) {
|
|
Packit |
14c646 |
my $res;
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$res = thaw freeze [$sub, $sub];
|
|
Packit |
14c646 |
is(int($res->[0]), int($res->[1]));
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$res = thaw freeze [$sclr, $sub, $sub, $sclr];
|
|
Packit |
14c646 |
is(int($res->[0]), int($res->[3]));
|
|
Packit |
14c646 |
is(int($res->[1]), int($res->[2]));
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
$res = thaw freeze [$sub, $sub, $sclr, $sclr];
|
|
Packit |
14c646 |
is(int($res->[0]), int($res->[1]));
|
|
Packit |
14c646 |
is(int($res->[2]), int($res->[3]));
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
{
|
|
Packit |
14c646 |
my @text = ("hello", "\x{a3}", "\x{a3} \x{2234}", "\x{2234}\x{2234}");
|
|
Packit |
14c646 |
|
|
Packit |
14c646 |
for my $text(@text) {
|
|
Packit |
14c646 |
my $res = (thaw freeze eval "sub {'" . $text . "'}")->();
|
|
Packit |
14c646 |
ok($res eq $text);
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
}
|
|
Packit |
14c646 |
|