title: How to safely use exec() in Python?
body: <p>I have been tasked with building an application where an end user can have custom rules to evaluate whether a returned query results in a warning or alert (based on there own thresholds).</p>
  
  <p>I've built a way for the user to template their logic. An example looks like this:</p>
  
  <pre><code>if (abs(&lt;&lt;21&gt;&gt;) &gt;= abs(&lt;&lt;22&gt;&gt;)):
      retVal = &lt;&lt;21&gt;&gt;
  else:
      retVal = &lt;&lt;22&gt;&gt;
  </code></pre>
  
  <p>The <code>&lt;&lt;21&gt;&gt;</code> and <code>&lt;&lt;22&gt;&gt;</code> parameters will be substituted with values found earlier in the program. Once all this substitution occurs I have a very simple if/else block (in this example) that looks like this stored in a variable (<code>execCd</code>):</p>
  
  <pre><code>if (abs(22.0) &gt;= abs(-162.0)):
      retVal = 22.0
  else:
      retVal = -162.0
  </code></pre>
  
  <p>This will <code>exec()</code> correctly. Now, how can I secure this? I've looked at this article: <a href="http://lybniz2.sourceforge.net/safeeval.html" rel="noreferrer">http://lybniz2.sourceforge.net/safeeval.html</a></p>
  
  <p>My code ends up looking like this:</p>
  
  <pre><code>safe_list = ['math','acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'de grees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'] 
  safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ]) 
  safe_dict['abs'] = abs
  exec(execCd,{"__builtins__":None},safe_dict)
  </code></pre>
  
  <p>However, the exec fails when I have the second and third parameter with this exception - <code>NameError: name 'retVal' is not defined</code></p>
  
  <p>Some of the custom logic the end users have is extensive and much of this changes on a fairly regular basis. I don't want to maintain their custom logic and end users want to be able to test various warning/alert threshold logic quickly. </p>
  
  <p>How can I secure this exec statement from unsafe (either intentional or unintentional) code?</p>