Skip to content

Commit

Permalink
feat(python): eval injection (CWE-95)
Browse files Browse the repository at this point in the history
  • Loading branch information
elsapet committed May 10, 2024
1 parent 94bbf03 commit 1f3ebac
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
65 changes: 65 additions & 0 deletions rules/python/lang/eval_using_user_input.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
imports:
- python_shared_common_user_input
patterns:
- pattern: eval($<...>$<USER_INPUT>$<...>)
filters:
- variable: USER_INPUT
detection: python_shared_common_user_input
scope: result
- pattern: $<LITERAL_EVAL>($<...>$<USER_INPUT>$<...>)
filters:
- variable: LITERAL_EVAL
detection: python_lang_eval_using_user_input_literal_eval
scope: result
- variable: USER_INPUT
detection: python_shared_common_user_input
scope: result
auxiliary:
- id: python_lang_eval_using_user_input_literal_eval
patterns:
- pattern: $<AST_LITERAL_EVAL>
filters:
- variable: AST_LITERAL_EVAL
regex: \A(ast\.)?literal_eval\z
languages:
- python
severity: critical
metadata:
description: "Unsanitized user input in 'eval' type function"
remediation_message: |-
## Description
Executing code with 'eval' or similar functions using unsanitized user input is risky and can lead to code injection vulnerabilities. This happens when external input is used directly in functions that execute code, allowing attackers to run malicious code within your application.
## Remediations
- **Do not** use `eval` or similar code execution functions with unsanitized user input. This can create a significant security risk by allowing code injection.
- **Do not** use `ast.literal_eval()` with unsanitized user input. While `literal_eval` is often considered to be less risky than `eval` because it evaluates strings as Python data structures only (integers, strings, dictionaries,etc), an attacker could exploit this function with deeply nested structures that could cause excessive memory allocation or stack consumption.
- **Do** use dynamic hardcoded values instead of direct user input to mitigate the risk of code injection. This approach allows for controlled execution of code without exposing your application to injected malicious code. For example, use a dictionary to store functions, and call these based on user input.
```python
def total_with_vat(a, b):
total = a + b
return total + total * 0.15
def total_without_vat(a, b):
return a + b
get_total = {
"incl_vat": total_with_vat,
"excl_vat": total_without_vat
}
if form.cleaned_data["include_vat"]:
total_func = get_total["incl_vat"]
total = total_func(a, b)
# ...
```
## References
- [OWASP Code injection explained](https://owasp.org/www-community/attacks/Code_Injection)
cwe_id:
- 95
id: python_lang_eval_using_user_input
documentation_url: https://docs.bearer.com/reference/rules/python_lang_eval_using_user_input
20 changes: 20 additions & 0 deletions tests/python/lang/eval_using_user_input/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const {
createNewInvoker,
getEnvironment,
} = require("../../../helper.js")
const { ruleId, ruleFile, testBase } = getEnvironment(__dirname)

describe(ruleId, () => {
const invoke = createNewInvoker(ruleId, ruleFile, testBase)

test("eval_using_user_input", () => {
const testCase = "main.py"

const results = invoke(testCase)

expect(results).toEqual({
Missing: [],
Extra: []
})
})
})
4 changes: 4 additions & 0 deletions tests/python/lang/eval_using_user_input/testdata/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def bad(request):
form = BadForm(request.POST)
# bearer:expected python_lang_eval_using_user_input
eval(form.cleaned_data["bad_eval"])

0 comments on commit 1f3ebac

Please sign in to comment.