-
Notifications
You must be signed in to change notification settings - Fork 196
fix: mitigate code injection related in #672 #681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Pull Request Test Coverage Report for Build 16203809193Details
💛 - Coveralls |
Tests are failing 👀 however they are on the right way I guess 😄 |
petl/util/base.py
Outdated
return eval("lambda rec: " + prog.sub(repl, s)) | ||
strexpr = "lambda rec: " + prog.sub(repl, expression_text) | ||
try: | ||
fun = eval(strexpr, { '__builtins__': None }, { '__builtins__': None }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imo it's still vulnerable, example:
"{__class__.__mro__[1].__subclasses__()[59]('/etc/passwd').read()}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you can use https://lmfit.github.io/asteval/ ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imo it's still vulnerable, example:
"{__class__.__mro__[1].__subclasses__()[59]('/etc/passwd').read()}"
You are correct.
I think this code wasn't meant to be used in unsafe environments.
Anyway, we have some possible approaches:
- Add code like the above to mitigate somewhat the issue for current users.
- Implement a second conditional version that uses
asteval
if available. - Deprecate the current mitigated code/approach and remove it in milestone v2.0 as it's planned for breaking changes.
Patches welcome. :)
petl/test/util/test_base.py
Outdated
res = fu(2) | ||
if res: | ||
print(res) | ||
assert exc_info is not None |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
petl/util/base.py
Outdated
from petl.comparison import comparable_itemgetter | ||
from petl.compat import ( |
Check warning
Code scanning / Prospector (reported by Codacy)
Redefining built-in 'next' (redefined-builtin)
fun = eval(strexpr, restricted, restricted) | ||
return fun | ||
except ValueError as ve: | ||
raise ValueError('Invalid expression: "%s" causes error: %s' % (strexpr, ve)) |
Check warning
Code scanning / Prospector (reported by Codacy)
Consider explicitly re-raising using 'raise ValueError('Invalid expression: "%s" causes error: %s' % (strexpr, ve)) from ve' (raise-missing-from)
fun = eval(strexpr, restricted, restricted) | ||
return fun | ||
except ValueError as ve: | ||
raise ValueError('Invalid expression: "%s" causes error: %s' % (strexpr, ve)) |
Check warning
Code scanning / Prospector (reported by Codacy)
Formatting a regular string which could be an f-string (consider-using-f-string)
@@ -330,3 +330,18 @@ | |||
table = [] | |||
with pytest.raises(FieldSelectionError): | |||
rowgroupby(table, 'foo') | |||
|
|||
|
|||
def test_expr_ok(): |
Check warning
Code scanning / Pylint (reported by Codacy)
Missing function docstring Warning test
assert res == 2 | ||
|
||
|
||
def test_expr_inject(): |
Check warning
Code scanning / Pylint (reported by Codacy)
Missing function docstring Warning test
@@ -1,19 +1,38 @@ | |||
from __future__ import absolute_import, print_function, division | |||
from __future__ import absolute_import, division, print_function |
Check warning
Code scanning / Pylint (reported by Codacy)
Missing module docstring Warning
} | ||
fun = eval(strexpr, restricted, restricted) | ||
return fun | ||
except ValueError as ve: |
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "ve" doesn't conform to snake_case naming style Warning
|
||
strexpr = "lambda rec: " + prog.sub(repl, expression_text) | ||
try: | ||
fun = eval(strexpr, _RESTRICTED, _RESTRICTED) |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of possibly insecure function - consider using safer ast.literal_eval. Warning
@@ -678,7 +700,32 @@ | |||
def repl(matchobj): | |||
return "rec['%s']" % matchobj.group(1) | |||
|
|||
return eval("lambda rec: " + prog.sub(repl, s)) | |||
global _RESTRICTED |
Check notice
Code scanning / Pylint (reported by Codacy)
Using the global statement Note
|
||
strexpr = "lambda rec: " + prog.sub(repl, expression_text) | ||
try: | ||
fun = eval(strexpr, _RESTRICTED, _RESTRICTED) |
Check warning
Code scanning / Pylint (reported by Codacy)
Use of eval Warning
fu = expr("{foo2} * {bar2}", trusted=True) | ||
with pytest.raises(KeyError) as exc_info: | ||
fu({'foo': 3, 'bar': 2}) | ||
assert exc_info is not None |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
# trick: using trusted=None will allow to skip using asteval | ||
fu = expr("__import__('os').system('ls')", trusted=None) | ||
fu({'foo': 3, 'bar': 2}) | ||
assert exc_info is not None |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
fu = expr("{foo2} * {bar2}", trusted=True) | ||
with pytest.raises(KeyError) as exc_info: | ||
fu({'foo': 3, 'bar': 2}) | ||
assert exc_info is not None |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
def _has_asteval(): | ||
if PY3: | ||
try: | ||
import asteval |
Check warning
Code scanning / Ruff (reported by Codacy)
asteval imported but unused; consider using importlib.util.find_spec to test for availability (F401) Warning test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pylint (reported by Codacy) found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pylintpython3 (reported by Codacy) found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
@@ -4,9 +4,9 @@ | |||
|
|||
from petl.errors import FieldSelectionError | |||
from petl.test.helpers import ieq, eq_ | |||
from petl.compat import next | |||
from petl.compat import PY3, next |
Check warning
Code scanning / Prospector (reported by Codacy)
Redefining built-in 'next' (redefined-builtin)
def _has_asteval(): | ||
if PY3: | ||
try: | ||
import asteval |
Check warning
Code scanning / Prospector (reported by Codacy)
Unused import asteval (unused-import)
def _has_asteval(): | ||
if PY3: | ||
try: | ||
import asteval |
Check warning
Code scanning / Prospector (reported by Codacy)
Import outside toplevel (asteval) (import-outside-toplevel)
|
||
|
||
def iterfieldmap(source, mappings, failonerror, errorvalue): | ||
def iterfieldmap(source, mappings, failonerror, errorvalue, trusted): |
Check warning
Code scanning / Prospector (reported by Codacy)
Too many branches (17/15) (too-many-branches)
|
||
|
||
def iterfieldmap(source, mappings, failonerror, errorvalue): | ||
def iterfieldmap(source, mappings, failonerror, errorvalue, trusted): |
Check warning
Code scanning / Prospector (reported by Codacy)
Too many local variables (18/15) (too-many-locals)
self.aeval.symtable['rec'] = rec | ||
evaluated = self.aeval("expr(rec)") | ||
if len(self.aeval.error) > 0: | ||
err = [ e.get_error()[-1] for e in self.aeval.error ] |
Check warning
Code scanning / Prospector (reported by Codacy)
Bad indentation. Found 16 spaces, expected 12 (bad-indentation)
evaluated = self.aeval("expr(rec)") | ||
if len(self.aeval.error) > 0: | ||
err = [ e.get_error()[-1] for e in self.aeval.error ] | ||
msg = "\n".join(err) |
Check warning
Code scanning / Prospector (reported by Codacy)
Bad indentation. Found 16 spaces, expected 12 (bad-indentation)
if len(self.aeval.error) > 0: | ||
err = [ e.get_error()[-1] for e in self.aeval.error ] | ||
msg = "\n".join(err) | ||
raise ValueError("Failed to evaluate expression due to: %s" % msg) |
Check warning
Code scanning / Prospector (reported by Codacy)
Bad indentation. Found 16 spaces, expected 12 (bad-indentation)
if len(self.aeval.error) > 0: | ||
err = [ e.get_error()[-1] for e in self.aeval.error ] | ||
msg = "\n".join(err) | ||
raise ValueError("Failed to evaluate expression due to: %s" % msg) |
Check warning
Code scanning / Prospector (reported by Codacy)
Formatting a regular string which could be an f-string (consider-using-f-string)
err = [ e.get_error()[-1] for e in self.aeval.error ] | ||
msg = "\n".join(err) | ||
raise ValueError("Failed to evaluate expression due to: %s" % msg) | ||
return evaluated |
Check warning
Code scanning / Prospector (reported by Codacy)
Bad indentation. Found 12 spaces, expected 8 (bad-indentation)
c937998
to
f0f522e
Compare
def test_json_inference(): | ||
data = [{'a': 1}, {'b': 2}, None] | ||
col = make_sqlalchemy_column(data, 'payload') | ||
assert col.name == 'payload' |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
data = [{'a': 1}, {'b': 2}, None] | ||
col = make_sqlalchemy_column(data, 'payload') | ||
assert col.name == 'payload' | ||
assert isinstance(col.type, JSON) |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
|
||
def test_int_inference(): | ||
col = make_sqlalchemy_column([1, 2, 3], 'n') | ||
assert col.name == 'n' |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
def test_int_inference(): | ||
col = make_sqlalchemy_column([1, 2, 3], 'n') | ||
assert col.name == 'n' | ||
assert isinstance(col.type, Integer) |
Check warning
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Warning test
This PR has the objective of .
Changes
util.base.expr
#672Checklist
Use this checklist to ensure the quality of pull requests that include new code and/or make changes to existing code.
tox
/pytest
master
branch and tested before sending the PR