3737# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3838# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3939# SOFTWARE.
40- """Small Rich + asteval demo for the GraalOS standalone sandbox."""
40+ """Small Rich demo for the GraalOS standalone sandbox."""
4141
4242from __future__ import annotations
4343
@@ -59,39 +59,12 @@ class EvalResult:
5959 ok : bool
6060 output : str
6161 elapsed_ms : float
62-
63-
64- def make_interpreter ():
65- from asteval import Interpreter
66- aeval = Interpreter ()
67- # Keep the demo's "safe" lane expression-oriented. GraalOS is the real
68- # containment boundary; this prevents the app-level evaluator from opening files.
69- aeval .symtable .pop ("open" , None )
70- return aeval
71-
72-
7362def render_message (role : str , body : str , style : str ) -> None :
7463 from rich .panel import Panel
7564 from rich .text import Text
7665 console .print (Panel (Text (body ), title = role , title_align = "left" , border_style = style ))
7766
7867
79- def safe_eval (aeval , expr : str ):
80- start = time .perf_counter ()
81- aeval .error = []
82- try :
83- value = aeval (expr )
84- except Exception as exc : # asteval normally records errors instead of raising
85- elapsed = (time .perf_counter () - start ) * 1000
86- return EvalResult ("asteval" , False , f"{ type (exc ).__name__ } : { exc } " , elapsed )
87-
88- elapsed = (time .perf_counter () - start ) * 1000
89- if aeval .error :
90- errors = "\n " .join (str (err .get_error ()) for err in aeval .error )
91- return EvalResult ("asteval" , False , errors , elapsed )
92- return EvalResult ("asteval" , True , repr (value ), elapsed )
93-
94-
9568def unsafe_eval (expr : str ):
9669 start = time .perf_counter ()
9770 try :
@@ -117,27 +90,23 @@ def render_result(result) -> None:
11790 render_message ("sandbox" , result .output , "green" if result .ok else "red" )
11891
11992
120- def evaluate (aeval , line : str ) -> None :
93+ def evaluate (line : str ) -> None :
12194 line = line .strip ()
12295 if not line :
12396 return
124- if line .startswith ("/unsafe " ):
125- expr = line [len ("/unsafe " ) :].strip ()
126- render_result (unsafe_eval (expr ))
127- else :
128- render_result (safe_eval (aeval , line ))
97+ render_result (unsafe_eval (line ))
12998
13099
131100def demo_script () -> list [str ]:
132101 return [
133102 "sum([i*i for i in range(1000)])" ,
134103 "sin(pi / 4) ** 2 + cos(pi / 4) ** 2" ,
135104 "open('/etc/passwd').read()" ,
136- "/unsafe open('/etc/passwd').read().splitlines()[:3]" ,
137- "/unsafe open('/etc/shadow').read()" ,
138- "/unsafe __import__('subprocess').run(['/bin/sh', '-c', 'id'], capture_output=True, text=True)" ,
139- "/unsafe __import__('socket').create_connection(('example.com', 80), timeout=2)" ,
140- "/unsafe __import__('ctypes').CDLL('libc.so').system(b'cat /etc/shadow')" ,
105+ "open('/etc/passwd').read().splitlines()[:3]" ,
106+ "open('/etc/shadow').read()" ,
107+ "__import__('subprocess').run(['/bin/sh', '-c', 'id'], capture_output=True, text=True)" ,
108+ "__import__('socket').create_connection(('example.com', 80), timeout=2)" ,
109+ "__import__('ctypes').CDLL('libc.so').system(b'cat /etc/shadow')" ,
141110 ]
142111
143112
@@ -146,10 +115,10 @@ def print_intro() -> None:
146115 """
147116 Type Python expressions and get chat-style results.
148117
149- Normal input uses asteval, a restricted expression evaluator.
150- Prefix with /unsafe to bypass asteval and use Python eval directly .
151- The process is still inside the GraalOS sandbox , so filesystem,
152- subprocess, native library, and network attempts remain contained.
118+ This demo treats each expression as untrusted Python code, such as
119+ code proposed by an LLM agent or pasted by a human operator .
120+ GraalOS sandboxes that code , so filesystem, subprocess, native
121+ library, and network attempts remain contained.
153122
154123 Commands: /demo, /help, /quit
155124 """
@@ -164,7 +133,6 @@ def print_help() -> None:
164133
165134
166135def interactive () -> int :
167- aeval = make_interpreter ()
168136 print_intro ()
169137 while True :
170138 try :
@@ -179,22 +147,20 @@ def interactive() -> int:
179147 print_help ()
180148 continue
181149 if command == "/demo" :
182- run_demo (aeval )
150+ run_demo ()
183151 continue
184- evaluate (aeval , line )
152+ evaluate (line )
185153
186154
187- def run_demo (aeval = None ) -> None :
188- if aeval is None :
189- aeval = make_interpreter ()
155+ def run_demo () -> None :
190156 for line in demo_script ():
191157 render_message ("you" , line , "blue" )
192- evaluate (aeval , line )
158+ evaluate (line )
193159
194160
195161def main (argv : list [str ] | None = None ) -> int :
196- global console , Console
197162 from rich .console import Console
163+ global console
198164 console = Console ()
199165 parser = argparse .ArgumentParser ()
200166 parser .add_argument ("--demo" , action = "store_true" , help = "run the prepared demo script and exit" )
@@ -219,13 +185,12 @@ def setUp(self):
219185 skip_unless_graalos ()
220186
221187 def test_demo_packages (self ):
222- import asteval
223188 import rich
224189
225- self .assertTrue (asteval .__version__ )
226190 self .assertTrue (rich .get_console ())
227191
228192 def test_sandbox_chat_demo (self ):
193+ from rich .console import Console
229194 global console
230195 output = io .StringIO ()
231196 console = Console (file = output , force_terminal = False , color_system = None , width = 120 )
0 commit comments