Embedding a restricted python interpreter

Michael Sparks zathras at thwackety.com
Fri Jan 7 00:50:33 EST 2005


Rolf Magnus wrote:

> I would like to embed a python interpreter within a program, but since
> that program would be able to automatically download scripts from the
> internet, I'd like to run those in a restricted environment, which
> basically means that I want to allow only a specific set of modules to be
> used by the scripts, so that it wouldn't be possible for them to remove
> files from the hard drive, kill processes or do other nasty stuff.
> Is there any way to do that with the standard python interpreter?

Current advice seems to be essentially "no". 

I've been pondering adding limited scripting to some personal apps I've
written and due to this toyed around with the idea of simple but parser
that only used ":" and whitespaces for indicating blocks with the aim of
being a generic/"universal"(*) language parser that could be used for many
little "languages". (ie no keywords, just "pure" structure)

   (*) By "universal" I mean something that allows a variety of different
       styles of syntax to be used, whilst technically still sharing the
       same underlying syntax. (Since that's a rather bogus statement,
       that's why it has quotes :)

In the end I sat down and wrote such a beast largely as a fun exercise. (It
uses PLY and is an SLR grammar) It *doesn't* have any backend so you get to
decided how restricted it can be, but, for example, the following code
would parse happily:
   (It's not quite python, but it's close syntactically)

class Grammar(object):
   from Lexer import Tokens as tokens
   precedence = ( ( "left", "DOT"))
   def p_error(self,p):
      print "Syntax error at", p
   end
end

This parses as follows:

A class function is provided with 3 arguments:
   * Grammar(object)
   * A code block
   * A lexical token "end" (Which could be anything)

The code block then contains 3 statements
   * The first is a function call, to a function called "from"
   * The second is an assignment statement
   * The third is a function call to the function "def" (which in turn takes
     3 arguments - a signature, a codeblock and a trailing token (the
     trailing token allows "else" clauses and try/except style blocks)

etc

However it will also parse happily:

EXPORT PROC compare(field::PTR TO person,type=>NIL) OF person:
   DEF result=FALSE
   IF type:
      SELECT type:
         CASE NAME:
            result:=compare_name(self.name,field)
         CASE PHONE:
            result:=compare_telephone(self.telephone,field)
         CASE ADDRESS:
            result:=compare_address(self.address,field)
         ENDCASES
      ENDSELECT
   ELSE:
      result:=compare_name(self.name,field,ORDER) # if type = NIL, ordering
   ENDIF
ENDPROC result

And also programs of the form:

shape square:
   pen down
   repeat 4:
      forward 10
      rotate 90
   end
   pen up
end

repeat (360/5):
   square()
   rotate 5
end

and so on.

If you're prepared to write backends to traverse an AST then you might find
it useful. (I also wrote the parser as an exercise in trying to generate a
parser in a test first manner)

If you're curious as to the sorts of languages it could parse the test cases
are here:
   * http://thwackety.com/viewcvs/viewcvs.cgi/Scratch/SWP/progs/

Some rather random examples are:
  29, A copy of the parser file at that point in time, but rewritten in a
      python-esque language parsable by the parser
  33, A simple program in a logo type language
  34, A simple program based on declarative l-systems for modelling
      biological growth systems.
  35, A simple SML-like language file implementing a stack
  37, An implementation of a "Person" object module in an Amiga-E like
      language.

(NB, here "language" means whatever AST a given backend might understand,
since they're all technically the same language)

http://thwackety.com/viewcvs/viewcvs.cgi/Scratch/SWP/README?rev=1.1

Describes the grammar, etc. (31 explicit rules, or alternatively 13
aggregate rules)

If you think it might be useful to you, feel free to do an anonymous
checkout:

cvs -d :pserver:anonymous at cerenity.org:2401/home/cvs/cvsroot login
cvs -d :pserver:anonymous at cerenity.org:2401/home/cvs/cvsroot co Scratch/SWP/

Since there is *no* backend at all at present this would be a bit of work.
(I've been tempted to investigate putting a lisp backend on the back, but
not found the time to do so. If I did though this would be a brackets free
lisp :) You can fine PLY here: http://systems.cs.uchicago.edu/ply/ .

Best Regards,


Michael.




More information about the Python-list mailing list