@@ -52,18 +52,24 @@ approach the speed of `eval` and the `numexpr` modules.
5252How Safe is asteval?
5353=======================
5454
55- Asteval avoids all of the exploits we know about that make :py:func: `eval `
56- dangerous. For reference, see, `Eval is really dangerous
57- <https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html> `_ and the
58- comments and links therein. From this discussion it is apparent that not only
59- is :py:func: `eval ` unsafe, but that it is a difficult prospect to make any
60- program that takes user input perfectly safe. In particular, if a user can
61- cause Python to crash with a segmentation fault, safety cannot be guaranteed.
62- Asteval explicitly forbids the exploits described in the above link, and works
63- hard to prevent malicious code from crashing Python or accessing the
64- underlying operating system. That said, we cannot guarantee that asteval is
65- completely safe from malicious code. We claim only that it is safer than the
66- builtin :py:func: `eval `, and that you might find it useful.
55+ Asteval avoids all of the exploits we know about that make
56+ :py:func: `eval ` dangerous. For reference, see, `Eval is really
57+ dangerous
58+ <https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html> `_
59+ and the comments and links therein. From this discussion it is
60+ apparent that not only is :py:func: `eval ` unsafe, but that it is a
61+ difficult prospect to make any program that takes user input perfectly
62+ safe. In particular, if a user can cause Python to crash with a
63+ segmentation fault, safety cannot be guaranteed. Asteval explicitly
64+ forbids the exploits described in the above link, and works hard to
65+ prevent malicious code from crashing Python or accessing the
66+ underlying operating system. That said, we cannot guarantee that
67+ asteval is completely safe from malicious code. We claim only that it
68+ is safer than the builtin :py:func: `eval `, and that you might find it
69+ useful. We also note that several other Python libraries that
70+ evaluate user-supplied expressions, including `numexpr ` and `sympy `
71+ use the builtin :py:func: `eval ` as part of their processing.
72+
6773
6874Some of the things not allowed in the asteval interpreter for safety reasons include:
6975
@@ -84,19 +90,22 @@ Some of the things not allowed in the asteval interpreter for safety reasons inc
8490In addition (and following the discussion in the link above), the following
8591attributes are blacklisted for all objects, and cannot be accessed:
8692
87- ``func_globals ``, ``func_code ``, ``func_closure ``,
88- ``im_class ``, ``im_func ``, ``im_self ``,
89- ``gi_code ``, ``gi_frame ``, `` f_locals ``
93+ ``func_globals ``, ``func_code ``, ``func_closure ``, `` im_class ``,
94+ ``im_func ``, ``im_self ``, ``gi_code ``, `` gi_frame ``, `` f_locals ``,
95+ ``__mro__ ``, ``_mro ``
9096
91- While this approach of making a blacklist cannot be guaranteed to be complete,
92- it does eliminate entire classes of attacks known to be able to seg-fault the
93- Python interpreter.
97+ [Note: this list may be incomplete - there may be other disallowed
98+ attributes]. While this approach of making a blacklist cannot be
99+ guaranteed to be complete, it does eliminate entire classes of attacks
100+ known to be able to seg-fault the Python interpreter or give access to
101+ the operating system.
94102
95- An important caveat is that asteval will typically expose numpy ``ufuncs `` from the
96- numpy module. Several of these can seg-fault Python without too much trouble.
97- If you are paranoid about safe user input that can never cause a segmentation
98- fault, you may want to consider disabling the use of numpy, or take extra care
99- to specify what can be used.
103+ An important caveat is that a typical use of asteval will import and
104+ expose numpy ``ufuncs `` from the numpy module. Several of these can
105+ seg-fault Python without too much trouble. If you safety from user
106+ input causing segmentation fault is a primary concern, you may want to
107+ consider disabling the use of numpy, or take extra care to specify
108+ what numpy functions can be used.
100109
101110In 2024, an independent security audit of asteval done by Andrew Effenhauser,
102111Ayman Hammad, and Daniel Crowley in the X-Force Security Research division of
@@ -108,10 +117,10 @@ needed, these modules can be added to any Interpreter either using the
108117``user_symbols `` argument when creating it, or adding the needed symbols to the
109118symbol table after the Interpreter is created.
110119
111- In 2025, a security audit by William Khem Marquez showed a
112- vulnerability from leaving some AST objects exposed within the
113- interpreter for user-defined functions ("Procedures"), and this was
114- fixed for version 1.0.6.
120+ In 2025, William Khem Marquez demonstrated two vulnerabilities: one
121+ from leaving some AST objects exposed within the interpreter for
122+ user-defined functions ("Procedures"), and one with f-string
123+ formatting. Both of these were fixed for version 1.0.6.
115124
116125There are other categories of safety that asteval may attempt to
117126address, but cannot guarantee success. The most important of these is
@@ -121,28 +130,33 @@ looking calculation such as::
121130
122131 from asteval import Interpreter
123132 aeval = Interpreter()
124- txt = """nmax = 1e8
133+ txt = """
134+ nmax = 1e8
125135 a = sqrt(arange(nmax)) # using numpy.sqrt() and numpy.arange()
126136 """
127137 aeval.eval(txt)
128138
129- can take a noticeable amount of CPU time - if it does not, increasing that
130- value of ``nmax `` almost certainly will, and can even crash the Python shell.
131-
132- As another example, consider the expression ``x**y**z ``. For values
133- ``x=y=z=5 ``, the run time will be well under 0.001 seconds. For ``x=y=z=8 ``,
134- run time will still be under 1 sec. Changing to ``x=8, y=9, z=9 ``, will cause
135- the statement to take several seconds. With ``x=y=z=9 ``, executing that
136- statement may take more than 1 hour on some machines. It is not hard to come
137- up with short program that would run for hundreds of years, which probably
138- exceeds anyones threshold for an acceptable run-time. There simply is not a
139- good way to predict how long any code will take to run from the text of the
140- code itself: run time cannot be determined lexically.
141-
142- To be clear, for the ``x**y**z `` exponentiation example, asteval will raise a
143- runtime error, telling you that an exponent > 10,000 is not allowed. Several
144- other attempts are made to prevent long-running operations or memory
145- exhaustion. These checks will prevent:
139+ can take a noticeable amount of CPU time - if it does not, increasing
140+ that value of ``nmax `` almost certainly will, and can even crash the
141+ Python shell.
142+
143+ As another example, and an illustration of the fundamental problem,
144+ consider the Python expression ``a = x**y**z ``. For values
145+ ``x=y=z=5 ``, the run time will be well under 0.001 seconds. For
146+ ``x=y=z=8 ``, run time will still be under 1 sec. Changing to ``x=8,
147+ y=9, z=9 ``, Python will ake several seconds (the value is :math: `\sim
148+ 10 ^{350 ,000 ,000 }`) With ``x=y=z=9 ``, executing that statement may take
149+ more than 1 hour on some machines. It is not hard to come up with
150+ short program that would run for hundreds of years, which probably
151+ exceeds everyones threshold for an acceptable run-time. The point
152+ here is tha there simply is not a good way to predict how long any
153+ code will take to run from the text of the code itself: run time
154+ cannot be determined lexically.
155+
156+ To be clear, for the ``x**y**z `` exponentiation example, asteval will
157+ raise a runtime error, telling you that an exponent > 10,000 is not
158+ allowed. Several other attempts are also made to prevent long-running
159+ operations or memory exhaustion. These checks will prevent:
146160
147161 * statements longer than 50,000 bytes.
148162 * values of exponents (``p `` in ``x**p ``) > 10,000.
@@ -151,11 +165,23 @@ exhaustion. These checks will prevent:
151165 * more than 262144 open buffers
152166 * opening a file with a mode other than ``'r' ``, ``'rb' ``, or ``'ru' ``.
153167
154- These checks happen at runtime, not by analyzing the text of the code. As with
155- the example above using ``numpy.arange ``, very large arrays and lists can be
156- created that might approach memory limits. There are countless other "clever
157- ways" to have very long run times that cannot be readily predicted from the
158- text.
168+ These checks happen at runtime, not by analyzing the text of the code.
169+ As with the example above using ``numpy.arange ``, very large arrays
170+ and lists can be created that might approach memory limits. There are
171+ countless other "clever ways" to have very long run times that cannot
172+ be readily predicted from the text of the code.
173+
174+ By default, the list of supported functions does include Python's
175+ ``open() `` -- in read-only mode -- which will allow disk access to the
176+ untrusted user. If ``numpy `` is supported, its ``load() `` and
177+ ``loadtxt() `` functions will also normally be supported. By itself,
178+ including these functions does not elevate permissions, and access is
179+ restricted to 'read-only mode'. Still, the user of the asteval
180+ interpreter would be able to read files with the privileges of the
181+ calling program. In some cases, this may not be desirable, and you
182+ may want to remove some of these functions from the symbol table,
183+ re-implement them, or ensure that your program cannot access
184+ information on disk that should be kept private.
159185
160186The exponential example also highlights the issue that there is not a good way
161187to check for a long-running calculation within a single Python process. That
@@ -188,15 +214,6 @@ executing expressions, with a code like this::
188214 with limited_recursion(100):
189215 Interpreter().eval(...)
190216
191- A secondary security concern is that the default list of supported functions
192- does include Python's ``open() `` which will allow disk access to the untrusted
193- user. If ``numpy `` is supported, its ``load() `` and ``loadtxt() `` functions will
194- also normally be supported. Including these functions does not elevate
195- permissions, but it does allow the user of the asteval interpreter to read
196- files with the privileges of the calling program. In some cases, this may not
197- be desirable, and you may want to remove some of these functions from the
198- symbol table, re-implement them, or ensure that your program cannot access
199- information on disk that should be kept private.
200217
201218In summary, while asteval attempts to be safe and is definitely safer than
202219using :py:func: `eval `, there may be ways that using asteval could lead to
0 commit comments