Files
Open_Ultra_SAST/src/openultrasast/ruleset/semantic/python.toml
T

148 lines
4.6 KiB
TOML
Raw Normal View History

version = "1"
language = "python"
[[source]]
id = "request"
patterns = [
"request.args",
"request.form",
"request.data",
"request.json",
"request.cookies",
"request.values",
"request.get_json",
"request.headers",
"request.files",
"request.get",
]
# Django and Django REST framework. The source vocabulary above is Flask's alone, so on a Django project no
# flow could begin anywhere: OpenCVE's notification test POSTs to a URL read from `form.cleaned_data`, and the
# scan answered the file and found nothing. `cleaned_data` is what a form bound to the request hands back --
# validated for type, not for trust.
[[source]]
id = "django-request"
patterns = [
"request.GET",
"request.POST",
"request.body",
"request.COOKIES",
"request.FILES",
"request.META",
"request.query_params",
"cleaned_data",
]
[[source]]
id = "argv"
patterns = ["sys.argv"]
[[source]]
id = "stdin"
patterns = ["sys.stdin", "stdin.read"]
[[source]]
id = "input"
patterns = ["input("]
[[sink]]
id = "eval"
cwe = "CWE-95"
calls = ["eval", "exec"]
rule_ids = ["python-unsafe-eval"]
[[sink]]
id = "pickle.loads"
cwe = "CWE-502"
calls = ["pickle.loads", "pickle.load", "yaml.load", "marshal.loads", "cPickle.loads"]
rule_ids = ["python-unsafe-deserialization"]
[[sink]]
id = "execute"
cwe = "CWE-89"
calls = ["execute", "executemany"]
rule_ids = ["python-sql-injection"]
[[sink]]
id = "os.system"
cwe = "CWE-78"
calls = ["os.system", "os.popen", "subprocess.run", "subprocess.call", "subprocess.Popen", "subprocess.check_output"]
rule_ids = ["python-os-command", "python-shell-true"]
[[sink]]
id = "hashlib"
cwe = "CWE-327"
calls = ["hashlib.md5", "hashlib.sha1", "hashlib.new"]
rule_ids = ["python-weak-hash"]
weak_literals = ["md5", "sha1", "MD5", "SHA1", "sha-1"]
[[sanitizer]]
id = "ast.literal_eval"
calls = ["ast.literal_eval"]
[[sanitizer]]
id = "yaml.safe_load"
calls = ["yaml.safe_load", "safe_load"]
[[sanitizer]]
id = "parameterized_execute"
calls = ["execute", "executemany"]
parameterized = true
# --- model-grounded-detection task 4.2 -------------------------------------------------------------------
# Sinks for the four families that previously had no fact routing to them by CWE, plus the ORM raw-SQL forms
# the Joern go/no-go measured as misses (11 of 23). Every entry below is a documented dangerous API of the
# named library, not an inferred pattern; the provenance is on each entry so a maintainer can review the data
# as data. `rule_ids` is left empty where no pattern rule exists: these facts feed the model layer, and a
# sink fact is not a detection rule.
[[sink]]
# CWE-89. Django's documented raw-SQL escape hatches (docs: "Performing raw SQL queries") and the
# SQLAlchemy/peewee text forms. These were the single largest miss class in the go/no-go.
id = "orm.raw"
cwe = "CWE-89"
calls = ["objects.raw", "raw", "RawSQL", "extra", "text", "from_statement"]
rule_ids = []
[[sink]]
# CWE-22. Filesystem entry points that accept a caller-supplied path: the stdlib openers plus Flask's
# send_file/send_from_directory and Django's FileResponse.
id = "path.open"
cwe = "CWE-22"
calls = ["open", "send_file", "send_from_directory", "FileResponse", "os.remove", "os.unlink", "shutil.copy", "shutil.move", "shutil.rmtree"]
rule_ids = []
[[sink]]
# CWE-79. The documented "this string is trusted HTML" escapes: Jinja/Flask render_template_string and
# Markup, Django's mark_safe and format_html. Rendering a template *file* is not a sink; rendering a
# caller-supplied *string* is.
id = "html.unescaped"
cwe = "CWE-79"
calls = ["render_template_string", "Markup", "mark_safe", "format_html", "HttpResponse"]
rule_ids = []
[[sink]]
# CWE-918 (SSRF) and CWE-601 (open redirect): outbound requests and redirects to a caller-supplied
# destination. requests/urllib/httpx are the documented HTTP clients; redirect is Flask's and Django's.
id = "outbound.request"
cwe = "CWE-918"
calls = ["requests.get", "requests.post", "requests.request", "urlopen", "urlretrieve", "httpx.get", "httpx.post"]
rule_ids = []
prefix_fixes_origin = true
[[sink]]
id = "redirect"
cwe = "CWE-601"
calls = ["redirect", "HttpResponseRedirect", "url_for"]
rule_ids = []
# Django's `reverse` builds an in-site path from a route NAME; request data can only fill its path segments.
prefix_fixes_origin = true
origin_anchors = ["reverse", "reverse_lazy"]
# Django's documented redirect-target check (`is_safe_url` is its pre-3.0 name). wger's fix for its open
# redirect is `if not url_has_allowed_host_and_scheme(next_url, ...): next_url = reverse(...)`.
[[sanitizer]]
id = "url_allowlist"
calls = ["url_has_allowed_host_and_scheme", "is_safe_url"]
guard = true