Safe
view release on metacpan or search on metacpan
package Safe;
use 5.003_11;
use Scalar::Util qw(reftype refaddr);
$Safe::VERSION = "2.35";
# *** Don't declare any lexicals above this point ***
#
# This function should return a closure which contains an eval that can't
# see any lexicals in scope (apart from __ExPr__ which is unavoidable)
sub lexless_anon_sub {
# $_[0] is package;
# $_[1] is strict flag;
my $__ExPr__ = $_[2]; # must be a lexical to create the closure that
# can be used to pass the value into the safe
# world
# Create anon sub ref in root of compartment.
# Uses a closure (on $__ExPr__) to pass in the code to be executed.
# (eval on one line to keep line numbers as expected by caller)
eval sprintf
'package %s; %s sub { @_=(); eval q[local *SIG; my $__ExPr__;] . $__ExPr__; }',
$_[0], $_[1] ? 'use strict;' : '';
}
use strict;
use Carp;
BEGIN { eval q{
use Carp::Heavy;
} }
use B ();
BEGIN {
no strict 'refs';
if (defined &B::sub_generation) {
*sub_generation = \&B::sub_generation;
}
else {
# fake sub generation changing for perls < 5.8.9
my $sg; *sub_generation = sub { ++$sg };
}
}
use Opcode 1.01, qw(
opset opset_to_ops opmask_add
empty_opset full_opset invert_opset verify_opset
opdesc opcodes opmask define_optag opset_to_hex
);
*ops_to_opset = \&opset; # Temporary alias for old Penguins
# Regular expressions and other unicode-aware code may need to call
# utf8->SWASHNEW (via perl's utf8.c). That will fail unless we share the
# SWASHNEW method.
# Sadly we can't just add utf8::SWASHNEW to $default_share because perl's
# utf8.c code does a fetchmethod on SWASHNEW to check if utf8.pm is loaded,
# and sharing makes it look like the method exists.
# The simplest and most robust fix is to ensure the utf8 module is loaded when
# Safe is loaded. Then we can add utf8::SWASHNEW to $default_share.
require utf8;
# we must ensure that utf8_heavy.pl, where SWASHNEW is defined, is loaded
# but without depending on too much knowledge of that implementation detail.
# This code (//i on a unicode string) should ensure utf8 is fully loaded
# and also loads the ToFold SWASH, unless things change so that these
# particular code points don't cause it to load.
# (Swashes are cached internally by perl in PL_utf8_* variables
# independent of being inside/outside of Safe. So once loaded they can be)
do { my $a = pack('U',0x100); my $b = chr 0x101; utf8::upgrade $b; $a =~ /$b/i };
# now we can safely include utf8::SWASHNEW in $default_share defined below.
my $default_root = 0;
# share *_ and functions defined in universal.c
# Don't share stuff like *UNIVERSAL:: otherwise code from the
# compartment can 0wn functions in UNIVERSAL
my $default_share = [qw[
*_
&PerlIO::get_layers
&UNIVERSAL::isa
&UNIVERSAL::can
&UNIVERSAL::VERSION
&utf8::is_utf8
&utf8::valid
&utf8::encode
&utf8::decode
&utf8::upgrade
&utf8::downgrade
&utf8::native_to_unicode
&utf8::unicode_to_native
&utf8::SWASHNEW
$version::VERSION
$version::CLASS
$version::STRICT
$version::LAX
@version::ISA
], ($] < 5.010 && qw[
&utf8::SWASHGET
]), ($] >= 5.008001 && qw[
&Regexp::DESTROY
]), ($] >= 5.010 && qw[
&re::is_regexp
&re::regname
&re::regnames
&re::regnames_count
&UNIVERSAL::DOES
&version::()
&version::new
&version::(""
&version::stringify
&version::(0+
&version::numify
my $pkg = $obj->root();
my ($stem, $leaf);
no strict 'refs';
$pkg = "main::$pkg\::"; # expand to full symbol table name
($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
# The 'my $foo' is needed! Without it you get an
# 'Attempt to free unreferenced scalar' warning!
my $stem_symtab = *{$stem}{HASH};
#warn "erase($pkg) stem=$stem, leaf=$leaf";
#warn " stem_symtab hash ".scalar(%$stem_symtab)."\n";
# ", join(', ', %$stem_symtab),"\n";
# delete $stem_symtab->{$leaf};
my $leaf_glob = $stem_symtab->{$leaf};
my $leaf_symtab = *{$leaf_glob}{HASH};
# warn " leaf_symtab ", join(', ', %$leaf_symtab),"\n";
%$leaf_symtab = ();
#delete $leaf_symtab->{'__ANON__'};
#delete $leaf_symtab->{'foo'};
#delete $leaf_symtab->{'main::'};
# my $foo = undef ${"$stem\::"}{"$leaf\::"};
if ($action and $action eq 'DESTROY') {
delete $stem_symtab->{$leaf};
} else {
$obj->share_from('main', $default_share);
}
1;
}
sub reinit {
my $obj= shift;
$obj->erase;
$obj->share_redo;
}
sub root {
my $obj = shift;
croak("Safe root method now read-only") if @_;
return $obj->{Root};
}
sub mask {
my $obj = shift;
return $obj->{Mask} unless @_;
$obj->deny_only(@_);
}
# v1 compatibility methods
sub trap { shift->deny(@_) }
sub untrap { shift->permit(@_) }
sub deny {
my $obj = shift;
$obj->{Mask} |= opset(@_);
}
sub deny_only {
my $obj = shift;
$obj->{Mask} = opset(@_);
}
sub permit {
my $obj = shift;
# XXX needs testing
$obj->{Mask} &= invert_opset opset(@_);
}
sub permit_only {
my $obj = shift;
$obj->{Mask} = invert_opset opset(@_);
}
sub dump_mask {
my $obj = shift;
print opset_to_hex($obj->{Mask}),"\n";
}
sub share {
my($obj, @vars) = @_;
$obj->share_from(scalar(caller), \@vars);
}
sub share_from {
my $obj = shift;
my $pkg = shift;
my $vars = shift;
my $no_record = shift || 0;
my $root = $obj->root();
croak("vars not an array ref") unless ref $vars eq 'ARRAY';
no strict 'refs';
# Check that 'from' package actually exists
croak("Package \"$pkg\" does not exist")
unless keys %{"$pkg\::"};
my $arg;
foreach $arg (@$vars) {
# catch some $safe->share($var) errors:
my ($var, $type);
$type = $1 if ($var = $arg) =~ s/^(\W)//;
# warn "share_from $pkg $type $var";
for (1..2) { # assign twice to avoid any 'used once' warnings
*{$root."::$var"} = (!$type) ? \&{$pkg."::$var"}
: ($type eq '&') ? \&{$pkg."::$var"}
: ($type eq '$') ? \${$pkg."::$var"}
: ($type eq '@') ? \@{$pkg."::$var"}
: ($type eq '%') ? \%{$pkg."::$var"}
: ($type eq '*') ? *{$pkg."::$var"}
: croak(qq(Can't share "$type$var" of unknown type));
}
}
$obj->share_record($pkg, $vars) unless $no_record or !$vars;
}
sub share_record {
my $obj = shift;
my $pkg = shift;
my $vars = shift;
my $shares = \%{$obj->{Shares} ||= {}};
# Record shares using keys of $obj->{Shares}. See reinit.
@{$shares}{@$vars} = ($pkg) x @$vars if @$vars;
}
sub share_redo {
my $obj = shift;
my $shares = \%{$obj->{Shares} ||= {}};
my($var, $pkg);
while(($var, $pkg) = each %$shares) {
# warn "share_redo $pkg\:: $var";
$obj->share_from($pkg, [ $var ], 1);
}
}
1;
__END__
=head1 NAME
Safe - Compile and execute code in restricted compartments
=head1 SYNOPSIS
use Safe;
$compartment = new Safe;
$compartment->permit(qw(time sort :browse));
$result = $compartment->reval($unsafe_code);
=head1 DESCRIPTION
The Safe extension module allows the creation of compartments
in which perl code can be evaluated. Each compartment has
=over 8
=item a new namespace
The "root" of the namespace (i.e. "main::") is changed to a
different package and code evaluated in the compartment cannot
refer to variables outside this namespace, even with run-time
glob lookups and other tricks.
Code which is compiled outside the compartment can choose to place
variables into (or I<share> variables with) the compartment's namespace
and only that data will be visible to code evaluated in the
compartment.
By default, the only variables shared with compartments are the
"underscore" variables $_ and @_ (and, technically, the less frequently
used %_, the _ filehandle and so on). This is because otherwise perl
operators which default to $_ will not work and neither will the
assignment of arguments to @_ on subroutine entry.
=item an operator mask
Each compartment has an associated "operator mask". Recall that
perl code is compiled into an internal format before execution.
Evaluating perl code (e.g. via "eval" or "do 'file'") causes
the code to be compiled into an internal format and then,
provided there was no error in the compilation, executed.
Code evaluated in a compartment compiles subject to the
compartment's operator mask. Attempting to evaluate code in a
compartment which contains a masked operator will cause the
compilation to fail with an error. The code will not be executed.
The default operator mask for a newly created compartment is
the ':default' optag.
It is important that you read the L<Opcode> module documentation
for more information, especially for detailed definitions of opnames,
optags and opsets.
Since it is only at the compilation stage that the operator mask
applies, controlled access to potentially unsafe operations can
be achieved by having a handle to a wrapper subroutine (written
outside the compartment) placed into the compartment. For example,
$cpt = new Safe;
sub wrapper {
# vet arguments and perform potentially unsafe operations
}
$cpt->share('&wrapper');
=back
=head1 WARNING
The authors make B<no warranty>, implied or otherwise, about the
suitability of this software for safety or security purposes.
The authors shall not in any case be liable for special, incidental,
consequential, indirect or other similar damages arising from the use
of this software.
Your mileage will vary. If in any doubt B<do not use it>.
=head1 METHODS
To create a new compartment, use
$cpt = new Safe;
Optional argument is (NAMESPACE), where NAMESPACE is the root namespace
to use for the compartment (defaults to "Safe::Root0", incremented for
each new compartment).
Note that version 1.00 of the Safe module supported a second optional
parameter, MASK. That functionality has been withdrawn pending deeper
consideration. Use the permit and deny methods described below.
The following methods can then be used on the compartment
object returned by the above constructor. The object argument
is implicit in each case.
=head2 permit (OP, ...)
Permit the listed operators to be used when compiling code in the
compartment (in I<addition> to any operators already permitted).
You can list opcodes by names, or use a tag name; see
L<Opcode/"Predefined Opcode Tags">.
=head2 permit_only (OP, ...)
Permit I<only> the listed operators to be used when compiling code in
the compartment (I<no> other operators are permitted).
=head2 deny (OP, ...)
( run in 1.585 second using v1.01-cache-2.11-cpan-71847e10f99 )