ManFromNeptune has asked for the wisdom of the Perl Monks concerning the following question:

Code snippit is as follows:

$a = "VAR_A"; $b = "print \"VAR_B (\$a)\\n\""; eval $b;
Is there any way to hide the $a variable from being visible within the eval? In fact, I'd like to hide from the eval'd code all variables and functions that were declared outside of it. Is this possible?

cheers
MFN

Replies are listed 'Best First'.
Re: limiting scope of 'eval'??
by edan (Curate) on Aug 22, 2004 at 07:20 UTC

    You might want to look at the Safe module, paying particular attention to the reval method:

    reval (STRING)

    This evaluates STRING as perl code inside the compartment.

    The code can only see the compartment's namespace (as returned by the root method). The compartment's root package appears to be the main:: package to the code inside the compartment.

    Update: I just saw sgifford's reply tucked away there, saying the same thing. :)

    --
    edan

Re: limiting scope of 'eval'??
by dragonchild (Archbishop) on Aug 22, 2004 at 03:16 UTC
    One possible idea would be to scope all your variables as package-scoped variables (not using 'my' at all) and then putting all your eval'ed code within another package. Something like the untested
    package Foo; $Foo::var1 = 'VAR_A'; my $pkg = '_eval_'; $Foo::var2 = <<"__END_EVAL__"; package $pkg; print "VAR_B( \$${pkg}::var1 )\\n"; __END_EVAL__ eval $Foo::var2;

    In other words, you could, but it would be very messy and would require you to code in a completely odd style. The issue you're running into is that eval'ed code is compiled and executed within the context that the eval() call has.

    More importantly - why are you wanting to do this? There most likely is a better way to do what you're trying to do ...

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: limiting scope of 'eval'??
by BrowserUk (Patriarch) on Aug 22, 2004 at 03:07 UTC

    Something like:

    $a = "VAR_A"; $b = qq[ package Nowhere; local ( $/, $_, $@, $0, $a, $b ); ## etc. print "VAR_B (\$a)\n"; ]; eval $b;

    I'm not quite sure what you expect from \$a though, except an error?


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

      You either have to use q instead of qq, or escape all your $ signs, otherwise they will be interpolated when you assign them to b instead of at eval time.

      So his use of \$a is correct afaik

Re: limiting scope of 'eval'??
by sgifford (Prior) on Aug 22, 2004 at 03:32 UTC
    If you want to completely prevent the eval'd code from accessing outside variables, maybe you should run it with the Safe module.
Re: limiting scope of 'eval'??
by SciDude (Friar) on Aug 22, 2004 at 03:18 UTC

    You appear to want no interpolation of $a. This is easily done with backticks:

    $a = "VAR_A"; $b = 'print \'VAR_B (\$a)\\n\''; eval $b;

    SciDude
    The first dog barks... all other dogs bark at the first dog.
Re: limiting scope of 'eval'??
by ikegami (Patriarch) on Aug 22, 2004 at 03:20 UTC
    In fact, I'd like to hide from the eval'd code all variables and functions that were declared outside of it. Is this possible?

    All 'my' variables can be hidden with the code below, but the others are trickier. If you can take the performance hit, use system to run the code in a seperate interpreter if you can.

    # Before any 'my'. Put in a seperate module if necessary. # Use eval_it instead of eval. sub eval_it { eval shift; }
      Ok, here are more details about the application...

      I've got a CGI program "index.pl". This script reads from a flat-file some of the content that will be printed as the web response. However, I want to be able to interpret macros (in perl) from the static file text... So

      flatfile:
      Today is <% $a = localtime(time); print $a; %>
      And index.pl is (basically):
      my $a = "important value"; # open flatfile here... # use index and substr to find macro strings delimited with <% ... %> # loop through found macros eval($macro) # replace macro code with eval value # print resulting text # keep using variable $a, expecting the value will still be "important + value"...
      So here, I'd like a user to be able to use their own variables, and not be able to (even accidentily) mess up the calling script's scoped variables/functions.

        I note a security implication here; you may have it covered.

        Can flatfile be edited to include:

        Kaboom! <% system(rm *) %>

        or other (possibly inadvertent) malicious code?

        Will index.pl run this code?

        Regards,

        PN5

        Perhaps your are re-inventing the wheel for the furtherance of your own knowledge, or as a homework assignment. Otherwise, I can't help but wonder why you aren't using one of the existing templating systems out there, like Template Toolkit (http://template-toolkit.org)... ?

        --
        edan

Re: limiting scope of 'eval'??
by eric256 (Parson) on Aug 22, 2004 at 07:05 UTC

    I just wanted to point out that I couldn't get any of the posted answers to work. Perhaps its a flaw in the way I implemented them so below is the code i tried and its output.

    use strict; use warnings; my $test = q[print $a;]; my $a = "doesn't stop me\n"; sub eval_it { eval shift; } { package safezone; sub eval_it { eval shift; } } warn "regular eval"; eval $test; warn "eval_it"; eval_it $test; warn "safezone::eval_it"; safezone::eval_it $test; warn "Prepend package"; eval "package safezone; $test"; __DATA__ regular eval at eval.pl line 18. doesn't stop me eval_it at eval.pl line 21. doesn't stop me safezone::eval_it at eval.pl line 24. doesn't stop me Prepend package at eval.pl line 27. doesn't stop me

    ___________
    Eric Hodges

      For your implementation of BrowserUK's solution, you forgot the local line that's needed to hide the variables:

      { package safezone; local $a; sub eval_it { eval shift; } }

      Of course since you need a declaration for all variables you want to hide, this can get cumbersome rather quickly, but it does work

      Update: This blows up with a Can't localize lexical variable $a at x line 12. Using my $a instead of local $a does seem to work here though.

      You need to move the sub declarations above the my declarations.
Re: limiting scope of 'eval'??
by Cody Pendant (Prior) on Aug 23, 2004 at 04:48 UTC
    I can't believe nobody's posted this -- try not to use $a and $b as variables, as they're used for sorting in Perl.


    ($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss')
    =~y~b-v~a-z~s; print

      Maybe because since at least 5.6.1, there isn't any real conflict.

      #! perl -slw use strict; my( $a, $b ) = ( 'mya', 'myb' ); { our( $a, $b ) = ( 'oura', 'ourb' ); my @sorted = sort { $a <=> $b } map{ rand 100 } 0 .. 99; print "$a : $b"; } print "$a : $b"; __END__ P:\test>test oura : ourb mya : myb

      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
        It's still best to avoid gratuitous use of $a and $b.

        When $a and $b are declared properly, no conflict. But if you forget to declare them, you can quietly get action at a distance.

        #! perl -slw use strict; ( $a, $b ) = ( 'mya', 'myb' ); # imagine several lines of intervening code { our( $a, $b ) = ( 'oura', 'ourb' ); my @sorted = sort { $a <=> $b } map{ rand 100 } 0 .. 99; print "$a : $b"; } print "$a : $b"; __END__ C:\s>perl sortab.pl oura : ourb oura : ourb #no warnings, $a and $b are quietly changed

        The script dies if they are declared (with my instead of our) in the same scope as the sort routine:

        #! perl -slw use strict; my( $a, $b ) = ( 'mya', 'myb' ); { my( $a, $b ) = ( 'oura', 'ourb' ); my @sorted = sort { $a <=> $b } map{ rand 100 } 0 .. 99; print "$a : $b"; } print "$a : $b"; __END__ C:\S\pl>perl sortab.pl Can't use "my $a" in sort comparison at sortab.pl line 10.
        Update: Deleted extra code tag