-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(python): add unsafe reflection rule (CWE-470) (#407)
- Loading branch information
Showing
4 changed files
with
109 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
imports: | ||
- python_shared_common_user_input | ||
- python_shared_lang_import1 | ||
patterns: | ||
- pattern: getattr($<_>, $<USER_INPUT>$<...>) | ||
filters: | ||
- variable: USER_INPUT | ||
detection: python_shared_common_user_input | ||
scope: result | ||
- pattern: $<GLOBALS>[$<USER_INPUT>]$<...> | ||
filters: | ||
- variable: GLOBALS | ||
detection: python_lang_reflection_using_user_input_globals | ||
scope: result | ||
- variable: USER_INPUT | ||
detection: python_shared_common_user_input | ||
scope: result | ||
- pattern: __import__($<USER_INPUT>) | ||
filters: | ||
- variable: USER_INPUT | ||
detection: python_shared_common_user_input | ||
scope: result | ||
- pattern: $<IMPORT>($<USER_INPUT>) | ||
filters: | ||
- variable: IMPORT | ||
detection: python_shared_lang_import1 | ||
scope: cursor | ||
filters: | ||
- variable: MODULE1 | ||
values: [importlib] | ||
- variable: NAME | ||
values: [import_module] | ||
- variable: USER_INPUT | ||
detection: python_shared_common_user_input | ||
scope: result | ||
auxiliary: | ||
- id: python_lang_reflection_using_user_input_globals | ||
patterns: | ||
- globals() | ||
languages: | ||
- python | ||
severity: high | ||
metadata: | ||
description: Usage of external input in code reflection | ||
remediation_message: | | ||
## Description | ||
Using external input for dynamic class loading or code execution through reflection poses a significant security risk. This practice can be exploited by attackers to load harmful classes or execute malicious methods, potentially resulting in remote code execution and other severe security threats. | ||
## Remediations | ||
- **Do not** - wherever possible - use external input with code reflection. Avoiding this practice altogether significantly lowers the risk of executing unauthorized or malicious code. | ||
- **Do** limit the allowed class names and method names to a predefined safelist. This approach restricts the scope of classes and methods that can be dynamically accessed, reducing the risk of unauthorized actions. | ||
- **Do** sanitize external input by removing special and unexpected characters that could facilitate code injection attacks. Characters such as single or double quotation marks and backslashes are particularly risky and should be filtered out. | ||
cwe_id: | ||
- 470 | ||
id: python_lang_reflection_using_user_input | ||
documentation_url: https://docs.bearer.com/reference/rules/python_lang_reflection_using_user_input |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("unsafe_reflection", () => { | ||
const testCase = "main.py" | ||
|
||
const results = invoke(testCase) | ||
|
||
expect(results).toEqual({ | ||
Missing: [], | ||
Extra: [] | ||
}) | ||
}) | ||
}) |
31 changes: 31 additions & 0 deletions
31
tests/python/lang/reflection_using_user_input/testdata/main.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
def bad(num1, num2): | ||
print("Enter the operation: add, subtract, multiply") | ||
operation = input() | ||
|
||
# bearer:expected python_lang_reflection_using_user_input | ||
result = globals()[operation](num1, num2) | ||
|
||
my_globals = globals() | ||
# bearer:expected python_lang_reflection_using_user_input | ||
my_globals[operation](num1, num2) | ||
|
||
def bad2(): | ||
user_action = input("what hack today? get_username or get_password") | ||
# bearer:expected python_lang_reflection_using_user_input | ||
getattr(current_user, user_action) | ||
|
||
import importlib | ||
|
||
def bad3(): | ||
user_module = input("which bad module to import today?") | ||
# bearer:expected python_lang_reflection_using_user_input | ||
__import__(user_module) | ||
|
||
# bearer:expected python_lang_reflection_using_user_input | ||
importlib.import_module(user_module) | ||
|
||
def ok(num1, num2): | ||
result = globals()["hard_coded_operation"](num1, num2) | ||
getattr(current_user, "known_action") | ||
__import__("safe_module") | ||
importlib.import_module("safe_module") |