Convert Python qlref tests to inline expectations

This commit is contained in:
Owen Mansel-Chan
2026-06-15 11:06:48 +01:00
parent d6ade8fe95
commit 0c2df7c7e9
475 changed files with 1612 additions and 1382 deletions

View File

@@ -1,3 +1,7 @@
#select
| django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | Cookie is constructed from a $@. | django_tests.py:11:26:11:32 | ControlFlowNode for request | user-supplied input |
edges
| django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:6:21:6:31 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
| django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:7:21:7:31 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep |
@@ -22,7 +26,3 @@ nodes
| django_tests.py:13:59:13:69 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| django_tests.py:13:59:13:82 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
subpaths
#select
| django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:6:21:6:43 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | django_tests.py:4:25:4:31 | ControlFlowNode for request | django_tests.py:7:21:7:44 | ControlFlowNode for Attribute() | Cookie is constructed from a $@. | django_tests.py:4:25:4:31 | ControlFlowNode for request | user-supplied input |
| django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | django_tests.py:11:26:11:32 | ControlFlowNode for request | django_tests.py:13:30:13:100 | ControlFlowNode for Fstring | Cookie is constructed from a $@. | django_tests.py:11:26:11:32 | ControlFlowNode for request | user-supplied input |

View File

@@ -1 +1,2 @@
Security/CWE-020/CookieInjection.ql
query: Security/CWE-020/CookieInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,20 +1,20 @@
import django.http
from django.urls import path
def django_response_bad(request):
def django_response_bad(request): # $ Source
resp = django.http.HttpResponse()
resp.set_cookie(request.GET.get("name"), # BAD: Cookie is constructed from user input
request.GET.get("value"))
resp.set_cookie(request.GET.get("name"), # $ Alert # BAD: Cookie is constructed from user input
request.GET.get("value")) # $ Alert
return resp
def django_response_bad2(request):
def django_response_bad2(request): # $ Source
response = django.http.HttpResponse()
response['Set-Cookie'] = f"{request.GET.get('name')}={request.GET.get('value')}; SameSite=None;" # BAD: Cookie header is constructed from user input.
response['Set-Cookie'] = f"{request.GET.get('name')}={request.GET.get('value')}; SameSite=None;" # $ Alert # BAD: Cookie header is constructed from user input.
return response
# fake setup, you can't actually run this
urlpatterns = [
path("response_bad", django_response_bad),
path("response_bd2", django_response_bad2)
]
]

View File

@@ -1 +1 @@
Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.ql
query: Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.ql

View File

@@ -1 +1,2 @@
Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.ql
query: Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,7 +2,7 @@ import hashlib
import hmac
import base64
from flask import Flask, request, make_response
from flask import Flask, request, make_response # $ Source
app = Flask(__name__)
SECRET_KEY = b"SECRET_KEY"
@@ -12,7 +12,7 @@ SECRET_KEY = b"SECRET_KEY"
def hmac_example():
data_raw = request.args.get("data").encode('utf-8')
data = base64.decodebytes(data_raw)
my_hmac = hmac.new(SECRET_KEY, data, hashlib.sha256)
my_hmac = hmac.new(SECRET_KEY, data, hashlib.sha256) # $ Alert
digest = my_hmac.digest()
print(digest)
return "ok"
@@ -22,7 +22,7 @@ def hmac_example():
def hmac_example2():
data_raw = request.args.get("data").encode('utf-8')
data = base64.decodebytes(data_raw)
my_hmac = hmac.new(key=SECRET_KEY, msg=data, digestmod=hashlib.sha256)
my_hmac = hmac.new(key=SECRET_KEY, msg=data, digestmod=hashlib.sha256) # $ Alert
digest = my_hmac.digest()
print(digest)
return "ok"
@@ -32,16 +32,16 @@ def hmac_example2():
def unknown_lib_1():
from unknown.lib import func
data = request.args.get("data")
func(data)
func(kw=data)
func(data) # $ Alert
func(kw=data) # $ Alert
@app.route("/unknown-lib-2")
def unknown_lib_2():
import unknown.lib
data = request.args.get("data")
unknown.lib.func(data)
unknown.lib.func(kw=data)
unknown.lib.func(data) # $ Alert
unknown.lib.func(kw=data) # $ Alert
def handle_this(arg, application = None):

View File

@@ -1 +1,2 @@
Security/CWE-020/IncompleteHostnameRegExp.ql
query: Security/CWE-020/IncompleteHostnameRegExp.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -3,7 +3,7 @@ import re
app = Flask(__name__)
UNSAFE_REGEX = re.compile("(www|beta).example.com/")
UNSAFE_REGEX = re.compile("(www|beta).example.com/") # $ Alert
SAFE_REGEX = re.compile(r"(www|beta)\.example\.com/")
@app.route('/some/path/bad')

View File

@@ -1 +1,2 @@
Security/CWE-020/IncompleteUrlSubstringSanitization.ql
query: Security/CWE-020/IncompleteUrlSubstringSanitization.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -6,13 +6,13 @@ app = Flask(__name__)
@app.route('/some/path/bad1')
def unsafe1(request):
target = request.args.get('target', '')
if "example.com" in target:
if "example.com" in target: # $ Alert
return redirect(target)
@app.route('/some/path/bad2')
def unsafe2(request):
target = request.args.get('target', '')
if target.endswith("example.com"):
if target.endswith("example.com"): # $ Alert
return redirect(target)

View File

@@ -1 +1,2 @@
Security/CWE-020/OverlyLargeRange.ql
query: Security/CWE-020/OverlyLargeRange.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,10 +1,10 @@
import re
overlap1 = re.compile(r'^[0-93-5]$') # NOT OK
overlap1 = re.compile(r'^[0-93-5]$') # $ Alert # NOT OK
overlap2 = re.compile(r'[A-ZA-z]') # NOT OK
overlap2 = re.compile(r'[A-ZA-z]') # $ Alert # NOT OK
isEmpty = re.compile(r'^[z-a]$') # NOT OK
isEmpty = re.compile(r'^[z-a]$') # $ Alert # NOT OK
isAscii = re.compile(r'^[\x00-\x7F]*$') # OK
@@ -14,18 +14,18 @@ codePoints = re.compile(r'[^\x21-\x7E]|[[\](){}<>/%]') # OK
NON_ALPHANUMERIC_REGEXP = re.compile(r'([^\#-~| |!])') # OK
smallOverlap = re.compile(r'[0-9a-fA-f]') # NOT OK
smallOverlap = re.compile(r'[0-9a-fA-f]') # $ Alert # NOT OK
weirdRange = re.compile(r'[$-`]') # NOT OK
weirdRange = re.compile(r'[$-`]') # $ Alert # NOT OK
keywordOperator = re.compile(r'[!\~\*\/%+-<>\^|=&]') # NOT OK
keywordOperator = re.compile(r'[!\~\*\/%+-<>\^|=&]') # $ Alert # NOT OK
notYoutube = re.compile(r'youtu\.be\/[a-z1-9.-_]+') # NOT OK
notYoutube = re.compile(r'youtu\.be\/[a-z1-9.-_]+') # $ Alert # NOT OK
numberToLetter = re.compile(r'[7-F]') # NOT OK
numberToLetter = re.compile(r'[7-F]') # $ Alert # NOT OK
overlapsWithClass1 = re.compile(r'[0-9\d]') # NOT OK
overlapsWithClass1 = re.compile(r'[0-9\d]') # $ Alert # NOT OK
overlapsWithClass2 = re.compile(r'[\w,.-?:*+]') # NOT OK
overlapsWithClass2 = re.compile(r'[\w,.-?:*+]') # $ Alert # NOT OK
unicodeStuff = re.compile('[\U0001D173-\U0001D17A\U000E0020-\U000E007F\U000e0001]') # NOT OK
unicodeStuff = re.compile('[\U0001D173-\U0001D17A\U000E0020-\U000E007F\U000e0001]') # $ Alert # NOT OK

View File

@@ -1,3 +1,13 @@
#select
| tarslip.py:15:1:15:3 | ControlFlowNode for tar | tarslip.py:14:7:14:39 | ControlFlowNode for Attribute() | tarslip.py:15:1:15:3 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:14:7:14:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:20:17:20:21 | ControlFlowNode for entry | tarslip.py:18:7:18:39 | ControlFlowNode for Attribute() | tarslip.py:20:17:20:21 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:18:7:18:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:39:17:39:21 | ControlFlowNode for entry | tarslip.py:35:7:35:39 | ControlFlowNode for Attribute() | tarslip.py:39:17:39:21 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:35:7:35:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:43:24:43:26 | ControlFlowNode for tar | tarslip.py:42:7:42:39 | ControlFlowNode for Attribute() | tarslip.py:43:24:43:26 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:42:7:42:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:61:21:61:25 | ControlFlowNode for entry | tarslip.py:58:7:58:39 | ControlFlowNode for Attribute() | tarslip.py:61:21:61:25 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:58:7:58:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:91:1:91:3 | ControlFlowNode for tar | tarslip.py:90:7:90:39 | ControlFlowNode for Attribute() | tarslip.py:91:1:91:3 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:90:7:90:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:96:17:96:21 | ControlFlowNode for entry | tarslip.py:94:7:94:39 | ControlFlowNode for Attribute() | tarslip.py:96:17:96:21 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:94:7:94:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:110:1:110:3 | ControlFlowNode for tar | tarslip.py:109:7:109:39 | ControlFlowNode for Attribute() | tarslip.py:110:1:110:3 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:109:7:109:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:113:24:113:26 | ControlFlowNode for tar | tarslip.py:112:7:112:39 | ControlFlowNode for Attribute() | tarslip.py:113:24:113:26 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:112:7:112:39 | ControlFlowNode for Attribute() | potentially untrusted source |
edges
| tarslip.py:14:1:14:3 | ControlFlowNode for tar | tarslip.py:15:1:15:3 | ControlFlowNode for tar | provenance | |
| tarslip.py:14:7:14:39 | ControlFlowNode for Attribute() | tarslip.py:14:1:14:3 | ControlFlowNode for tar | provenance | |
@@ -54,13 +64,3 @@ nodes
| tarslip.py:112:7:112:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| tarslip.py:113:24:113:26 | ControlFlowNode for tar | semmle.label | ControlFlowNode for tar |
subpaths
#select
| tarslip.py:15:1:15:3 | ControlFlowNode for tar | tarslip.py:14:7:14:39 | ControlFlowNode for Attribute() | tarslip.py:15:1:15:3 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:14:7:14:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:20:17:20:21 | ControlFlowNode for entry | tarslip.py:18:7:18:39 | ControlFlowNode for Attribute() | tarslip.py:20:17:20:21 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:18:7:18:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:39:17:39:21 | ControlFlowNode for entry | tarslip.py:35:7:35:39 | ControlFlowNode for Attribute() | tarslip.py:39:17:39:21 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:35:7:35:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:43:24:43:26 | ControlFlowNode for tar | tarslip.py:42:7:42:39 | ControlFlowNode for Attribute() | tarslip.py:43:24:43:26 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:42:7:42:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:61:21:61:25 | ControlFlowNode for entry | tarslip.py:58:7:58:39 | ControlFlowNode for Attribute() | tarslip.py:61:21:61:25 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:58:7:58:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:91:1:91:3 | ControlFlowNode for tar | tarslip.py:90:7:90:39 | ControlFlowNode for Attribute() | tarslip.py:91:1:91:3 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:90:7:90:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:96:17:96:21 | ControlFlowNode for entry | tarslip.py:94:7:94:39 | ControlFlowNode for Attribute() | tarslip.py:96:17:96:21 | ControlFlowNode for entry | This file extraction depends on a $@. | tarslip.py:94:7:94:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:110:1:110:3 | ControlFlowNode for tar | tarslip.py:109:7:109:39 | ControlFlowNode for Attribute() | tarslip.py:110:1:110:3 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:109:7:109:39 | ControlFlowNode for Attribute() | potentially untrusted source |
| tarslip.py:113:24:113:26 | ControlFlowNode for tar | tarslip.py:112:7:112:39 | ControlFlowNode for Attribute() | tarslip.py:113:24:113:26 | ControlFlowNode for tar | This file extraction depends on a $@. | tarslip.py:112:7:112:39 | ControlFlowNode for Attribute() | potentially untrusted source |

View File

@@ -1 +1,2 @@
Security/CWE-022/TarSlip.ql
query: Security/CWE-022/TarSlip.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -11,13 +11,13 @@ tar = tarfile.open(safe_filename_tar)
for entry in tar:
tar.extract(entry)
tar = tarfile.open(unsafe_filename_tar)
tar.extractall()
tar = tarfile.open(unsafe_filename_tar) # $ Source
tar.extractall() # $ Alert
tar.close()
tar = tarfile.open(unsafe_filename_tar)
tar = tarfile.open(unsafe_filename_tar) # $ Source
for entry in tar:
tar.extract(entry)
tar.extract(entry) # $ Alert
tar = tarfile.open(safe_filename_tar)
tar.extractall()
@@ -32,15 +32,15 @@ for entry in tar:
tar.extract(entry, "/tmp/unpack/")
#Part Sanitized
tar = tarfile.open(unsafe_filename_tar)
tar = tarfile.open(unsafe_filename_tar) # $ Source
for entry in tar:
if ".." in entry.name:
raise ValueError("Illegal tar archive entry")
tar.extract(entry, "/tmp/unpack/")
tar.extract(entry, "/tmp/unpack/") # $ Alert
#Unsanitized members
tar = tarfile.open(unsafe_filename_tar)
tar.extractall(members=tar)
tar = tarfile.open(unsafe_filename_tar) # $ Source
tar.extractall(members=tar) # $ Alert
#Sanitize members
@@ -55,10 +55,10 @@ tar.extractall(members=safemembers(tar))
# Wrong sanitizer (is missing not)
tar = tarfile.open(unsafe_filename_tar)
tar = tarfile.open(unsafe_filename_tar) # $ Source
for entry in tar:
if os.path.isabs(entry.name) or ".." in entry.name:
tar.extract(entry, "/tmp/unpack/")
tar.extract(entry, "/tmp/unpack/") # $ Alert
# OK Sanitized using not
@@ -87,13 +87,13 @@ for entry in tar:
extraction_filter = "fully_trusted"
tar = tarfile.open(unsafe_filename_tar)
tar.extractall(filter=extraction_filter) # unsafe
tar = tarfile.open(unsafe_filename_tar) # $ Source
tar.extractall(filter=extraction_filter) # $ Alert # unsafe
tar.close()
tar = tarfile.open(unsafe_filename_tar)
tar = tarfile.open(unsafe_filename_tar) # $ Source
for entry in tar:
tar.extract(entry, filter=extraction_filter) # unsafe
tar.extract(entry, filter=extraction_filter) # $ Alert # unsafe
extraction_filter = "data"
@@ -106,11 +106,11 @@ for entry in tar:
tar.extract(entry, filter=extraction_filter) # safe
extraction_filter = None
tar = tarfile.open(unsafe_filename_tar)
tar.extractall(filter=extraction_filter) # unsafe
tar = tarfile.open(unsafe_filename_tar) # $ Source
tar.extractall(filter=extraction_filter) # $ Alert # unsafe
tar = tarfile.open(unsafe_filename_tar)
tar.extractall(members=tar, filter=extraction_filter) # unsafe
tar = tarfile.open(unsafe_filename_tar) # $ Source
tar.extractall(members=tar, filter=extraction_filter) # $ Alert # unsafe
tar = tarfile.open(unsafe_filename_tar)
tar.extractall(members=safemembers(tar), filter=extraction_filter) # safe -- we assume `safemembers` makes up for the unsafe filter

View File

@@ -4,21 +4,21 @@ from jinja2 import Template
from jinja2 import Environment, DictLoader, escape
def a(request):
def a(request): # $ Source
# Load the template
template = request.GET['template']
t = Template(template) # BAD: Template constructed from user input
t = Template(template) # $ Alert # BAD: Template constructed from user input
name = request.GET['name']
# Render the template with the context data
html = t.render(name=escape(name))
return HttpResponse(html)
def b(request):
def b(request): # $ Source
import jinja2
# Load the template
template = request.GET['template']
env = Environment()
t = env.from_string(template) # BAD: Template constructed from user input
t = env.from_string(template) # $ Alert # BAD: Template constructed from user input
name = request.GET['name']
# Render the template with the context data
html = t.render(name=escape(name))

View File

@@ -1,3 +1,6 @@
#select
| JinjaSsti.py:10:18:10:25 | ControlFlowNode for template | JinjaSsti.py:7:7:7:13 | ControlFlowNode for request | JinjaSsti.py:10:18:10:25 | ControlFlowNode for template | This template construction depends on a $@. | JinjaSsti.py:7:7:7:13 | ControlFlowNode for request | user-provided value |
| JinjaSsti.py:21:25:21:32 | ControlFlowNode for template | JinjaSsti.py:16:7:16:13 | ControlFlowNode for request | JinjaSsti.py:21:25:21:32 | ControlFlowNode for template | This template construction depends on a $@. | JinjaSsti.py:16:7:16:13 | ControlFlowNode for request | user-provided value |
edges
| JinjaSsti.py:7:7:7:13 | ControlFlowNode for request | JinjaSsti.py:9:5:9:12 | ControlFlowNode for template | provenance | AdditionalTaintStep |
| JinjaSsti.py:9:5:9:12 | ControlFlowNode for template | JinjaSsti.py:10:18:10:25 | ControlFlowNode for template | provenance | |
@@ -11,6 +14,3 @@ nodes
| JinjaSsti.py:19:5:19:12 | ControlFlowNode for template | semmle.label | ControlFlowNode for template |
| JinjaSsti.py:21:25:21:32 | ControlFlowNode for template | semmle.label | ControlFlowNode for template |
subpaths
#select
| JinjaSsti.py:10:18:10:25 | ControlFlowNode for template | JinjaSsti.py:7:7:7:13 | ControlFlowNode for request | JinjaSsti.py:10:18:10:25 | ControlFlowNode for template | This template construction depends on a $@. | JinjaSsti.py:7:7:7:13 | ControlFlowNode for request | user-provided value |
| JinjaSsti.py:21:25:21:32 | ControlFlowNode for template | JinjaSsti.py:16:7:16:13 | ControlFlowNode for request | JinjaSsti.py:21:25:21:32 | ControlFlowNode for template | This template construction depends on a $@. | JinjaSsti.py:16:7:16:13 | ControlFlowNode for request | user-provided value |

View File

@@ -1 +1,2 @@
Security/CWE-074/TemplateInjection.ql
query: Security/CWE-074/TemplateInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
Security/CWE-078/CommandInjection.ql
query: Security/CWE-078/CommandInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,7 +2,7 @@ import os
import platform
import popen2
from flask import Flask, request
from flask import Flask, request # $ Source
app = Flask(__name__)
@@ -16,14 +16,14 @@ def python2_specific():
"""
files = request.args.get("files", "")
os.popen2("ls " + files)
os.popen3("ls " + files)
os.popen4("ls " + files)
os.popen2("ls " + files) # $ Alert
os.popen3("ls " + files) # $ Alert
os.popen4("ls " + files) # $ Alert
platform.popen("ls " + files)
platform.popen("ls " + files) # $ Alert
popen2.popen2("ls " + files)
popen2.popen3("ls " + files)
popen2.popen4("ls " + files)
popen2.Popen3("ls " + files)
popen2.Popen4("ls " + files)
popen2.popen2("ls " + files) # $ Alert
popen2.popen3("ls " + files) # $ Alert
popen2.popen4("ls " + files) # $ Alert
popen2.Popen3("ls " + files) # $ Alert
popen2.Popen4("ls " + files) # $ Alert

View File

@@ -1 +1 @@
Security/CWE-078/CommandInjection.ql
query: Security/CWE-078/CommandInjection.ql

View File

@@ -1 +1 @@
Security/CWE-078/UnsafeShellCommandConstruction.ql
query: Security/CWE-078/UnsafeShellCommandConstruction.ql

View File

@@ -1 +1,2 @@
Security/CWE-079/Jinja2WithoutEscaping.ql
query: Security/CWE-079/Jinja2WithoutEscaping.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -6,7 +6,7 @@ from jinja2 import Environment, select_autoescape, FileSystemLoader, Template
app = Flask(__name__)
loader = FileSystemLoader( searchpath="templates/" )
unsafe_env = Environment(loader=loader)
unsafe_env = Environment(loader=loader) # $ Alert
safe1_env = Environment(loader=loader, autoescape=True)
safe2_env = Environment(loader=loader, autoescape=select_autoescape())
@@ -38,18 +38,18 @@ e = Environment(
auto = select_autoescape
e = Environment(autoescape=auto) # GOOD
z = 0
e = Environment(autoescape=z) # BAD
e = Environment(autoescape=z) # $ Alert # BAD
E = Environment
E() # BAD
E(autoescape=z) # BAD
E() # $ Alert # BAD
E(autoescape=z) # $ Alert # BAD
E(autoescape=auto) # GOOD
E(autoescape=0+1) # GOOD
def checked(cond=False):
if cond:
e = Environment(autoescape=cond) # GOOD
e = Environment(autoescape=cond) # $ Alert # GOOD
unsafe_tmpl = Template('Hello {{ name }}!')
unsafe_tmpl = Template('Hello {{ name }}!') # $ Alert
safe1_tmpl = Template('Hello {{ name }}!', autoescape=True)
safe2_tmpl = Template('Hello {{ name }}!', autoescape=select_autoescape())

View File

@@ -1,3 +1,7 @@
#select
| reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr | Cross-site scripting vulnerability due to a $@. | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to a $@. | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to a $@. | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
edges
| reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | reflected_xss.py:2:26:2:32 | ControlFlowNode for request | provenance | |
| reflected_xss.py:2:26:2:32 | ControlFlowNode for request | reflected_xss.py:9:18:9:24 | ControlFlowNode for request | provenance | |
@@ -28,7 +32,3 @@ nodes
| reflected_xss.py:27:23:27:29 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
subpaths
#select
| reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | reflected_xss.py:10:26:10:53 | ControlFlowNode for BinaryExpr | Cross-site scripting vulnerability due to a $@. | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | reflected_xss.py:22:26:22:41 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to a $@. | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | reflected_xss.py:28:26:28:41 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to a $@. | reflected_xss.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |

View File

@@ -1 +1,2 @@
Security/CWE-079/ReflectedXss.ql
query: Security/CWE-079/ReflectedXss.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,5 +1,5 @@
import json
from flask import Flask, request, make_response, escape, jsonify
from flask import Flask, request, make_response, escape, jsonify # $ Source
app = Flask(__name__)
@@ -7,7 +7,7 @@ app = Flask(__name__)
@app.route("/unsafe")
def unsafe():
first_name = request.args.get("name", "")
return make_response("Your name is " + first_name) # NOT OK
return make_response("Your name is " + first_name) # $ Alert # NOT OK
@app.route("/safe")
@@ -19,13 +19,13 @@ def safe():
@app.route("/unsafe/json")
def unsafe_json():
data = json.loads(request.data)
return make_response(json.dumps(data)) # NOT OK
return make_response(json.dumps(data)) # $ Alert # NOT OK
@app.route("/safe/json")
def safe_json():
data = json.loads(request.data)
return make_response(json.dumps(data), 200, {'Content-Type': 'application/json'}) # OK, FP
return make_response(json.dumps(data), 200, {'Content-Type': 'application/json'}) # $ SPURIOUS: Alert # OK, FP
@app.route("/jsonify")

View File

@@ -1,2 +1,4 @@
query: Security/CWE-089/SqlInjection.ql
postprocess: utils/test/PrettyPrintModels.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql

View File

@@ -3,4 +3,4 @@ import sys
import psycopg
conn = psycopg.connect(...)
conn.execute(sys.argv[1])
conn.execute(sys.argv[1]) # $ Alert

View File

@@ -1 +1,2 @@
Security/CWE-089/SqlInjection.ql
query: Security/CWE-089/SqlInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -11,19 +11,19 @@ class User(models.Model):
pass
@app.route("/users/<username>")
def show_user(username):
def show_user(username): # $ Source
with connection.cursor() as cursor:
# GOOD -- Using parameters
cursor.execute("SELECT * FROM users WHERE username = %s", username)
User.objects.raw("SELECT * FROM users WHERE username = %s", (username,))
# BAD -- Using string formatting
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username) # $ Alert
# BAD -- other ways of executing raw SQL code with string interpolation
User.objects.annotate(RawSQL("insert into names_file ('name') values ('%s')" % username))
User.objects.raw("insert into names_file ('name') values ('%s')" % username)
User.objects.extra("insert into names_file ('name') values ('%s')" % username)
User.objects.annotate(RawSQL("insert into names_file ('name') values ('%s')" % username)) # $ Alert
User.objects.raw("insert into names_file ('name') values ('%s')" % username) # $ Alert
User.objects.extra("insert into names_file ('name') values ('%s')" % username) # $ Alert
# BAD (but currently no custom query to find this)
#

View File

@@ -20,15 +20,15 @@ class User(Base):
@app.route("/users/<username>")
def show_user(username):
def show_user(username): # $ Source
session = sqlalchemy.orm.Session(engine)
# BAD, normal SQL injection
stmt = sqlalchemy.text("SELECT * FROM users WHERE username = '{}'".format(username))
stmt = sqlalchemy.text("SELECT * FROM users WHERE username = '{}'".format(username)) # $ Alert
results = session.execute(stmt).fetchall()
# BAD, allows SQL injection
username_formatted_for_sql = sqlalchemy.text("'{}'".format(username))
username_formatted_for_sql = sqlalchemy.text("'{}'".format(username)) # $ Alert
stmt = sqlalchemy.select(User).where(User.username == username_formatted_for_sql)
results = session.execute(stmt).scalars().all()
@@ -38,14 +38,14 @@ def show_user(username):
# All of these should be flagged by query
t1 = sqlalchemy.text(username)
t2 = sqlalchemy.text(text=username)
t3 = sqlalchemy.sql.text(username)
t4 = sqlalchemy.sql.text(text=username)
t5 = sqlalchemy.sql.expression.text(username)
t6 = sqlalchemy.sql.expression.text(text=username)
t7 = sqlalchemy.sql.expression.TextClause(username)
t8 = sqlalchemy.sql.expression.TextClause(text=username)
t1 = sqlalchemy.text(username) # $ Alert
t2 = sqlalchemy.text(text=username) # $ Alert
t3 = sqlalchemy.sql.text(username) # $ Alert
t4 = sqlalchemy.sql.text(text=username) # $ Alert
t5 = sqlalchemy.sql.expression.text(username) # $ Alert
t6 = sqlalchemy.sql.expression.text(text=username) # $ Alert
t7 = sqlalchemy.sql.expression.TextClause(username) # $ Alert
t8 = sqlalchemy.sql.expression.TextClause(text=username) # $ Alert
t9 = db.text(username)
t10 = db.text(text=username)
t9 = db.text(username) # $ Alert
t10 = db.text(text=username) # $ Alert

View File

@@ -1 +1,2 @@
Security/CWE-090/LdapInjection.ql
query: Security/CWE-090/LdapInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,4 +1,4 @@
from flask import request, Flask
from flask import request, Flask # $ Source
import ldap3
app = Flask(__name__)
@@ -18,7 +18,7 @@ def normal():
srv = ldap3.Server('ldap://127.0.0.1')
conn = ldap3.Connection(srv, user=dn, auto_bind=True)
conn.search(dn, search_filter)
conn.search(dn, search_filter) # $ Alert
@app.route("/direct")
@@ -35,7 +35,7 @@ def direct():
srv = ldap3.Server('ldap://127.0.0.1')
conn = ldap3.Connection(srv, user=dn, auto_bind=True).search(
dn, search_filter)
dn, search_filter) # $ Alert
# if __name__ == "__main__":
# app.run(debug=True)

View File

@@ -1,4 +1,4 @@
from flask import request, Flask
from flask import request, Flask # $ Source
import ldap
app = Flask(__name__)
@@ -18,7 +18,7 @@ def normal():
ldap_connection = ldap.initialize("ldap://127.0.0.1")
user = ldap_connection.search_s(
dn, ldap.SCOPE_SUBTREE, search_filter)
dn, ldap.SCOPE_SUBTREE, search_filter) # $ Alert
@app.route("/direct")
@@ -34,7 +34,7 @@ def direct():
search_filter = "(user={})".format(unsafe_filter)
user = ldap.initialize("ldap://127.0.0.1").search_s(
dn, ldap.SCOPE_SUBTREE, search_filter)
dn, ldap.SCOPE_SUBTREE, search_filter) # $ Alert
@app.route("/normal_argbyname")
@@ -52,7 +52,7 @@ def normal_argbyname():
ldap_connection = ldap.initialize("ldap://127.0.0.1")
user = ldap_connection.search_s(
dn, ldap.SCOPE_SUBTREE, filterstr=search_filter)
dn, ldap.SCOPE_SUBTREE, filterstr=search_filter) # $ Alert
# if __name__ == "__main__":

View File

@@ -1 +1,2 @@
Security/CWE-113/HeaderInjection.ql
query: Security/CWE-113/HeaderInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1 +1,2 @@
Security/CWE-116/BadTagFilter.ql
query: Security/CWE-116/BadTagFilter.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,28 +1,28 @@
import re
filters = [
re.compile(r"""<script.*?>.*?<\/script>""", re.IGNORECASE), # NOT OK - doesn't match newlines or `</script >`
re.compile(r"""<script.*?>.*?<\/script>""", re.IGNORECASE | re.DOTALL), # NOT OK - doesn't match `</script >`
re.compile(r"""<script.*?>.*?<\/script>""", re.IGNORECASE), # $ Alert # NOT OK - doesn't match newlines or `</script >`
re.compile(r"""<script.*?>.*?<\/script>""", re.IGNORECASE | re.DOTALL), # $ Alert # NOT OK - doesn't match `</script >`
re.compile(r"""<script.*?>.*?<\/script[^>]*>""", re.IGNORECASE | re.DOTALL), # OK
re.compile(r"""<!--.*-->""", re.IGNORECASE | re.DOTALL), # OK - we don't care regexps that only match comments
re.compile(r"""<!--.*--!?>""", re.IGNORECASE | re.DOTALL), # OK
re.compile(r"""<!--.*--!?>""", re.IGNORECASE), # NOT OK, does not match newlines
re.compile(r"""<!--.*--!?>""", re.IGNORECASE), # $ Alert # NOT OK, does not match newlines
re.compile(r"""(?is)<!--.*--!?>"""), # OK
re.compile(r"""(?i)<!--.*--!?>"""), # NOT OK, does not match newlines [NOT DETECTED]
re.compile(r"""<script.*?>(.|\s)*?<\/script[^>]*>""", re.IGNORECASE), # NOT OK - doesn't match inside the script tag
re.compile(r"""<script[^>]*?>.*?<\/script[^>]*>""", re.IGNORECASE), # NOT OK - doesn't match newlines inside the content
re.compile(r"""<script(\s|\w|=|")*?>.*?<\/script[^>]*>""", re.IGNORECASE | re.DOTALL), # NOT OK - does not match single quotes for attribute values
re.compile(r"""<script(\s|\w|=|')*?>.*?<\/script[^>]*>""", re.IGNORECASE | re.DOTALL), # NOT OK - does not match double quotes for attribute values
re.compile(r"""<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>""", re.IGNORECASE | re.DOTALL), # NOT OK - does not match tabs between attributes
re.compile(r"""<script.*?>.*?<\/script[^>]*>""", re.re.DOTALL), # NOT OK - does not match uppercase SCRIPT tags
re.compile(r"""<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>""", re.DOTALL), # NOT OK - does not match mixed case script tags
re.compile(r"""<script[^>]*?>[\s\S]*?<\/script.*>""", re.IGNORECASE), # NOT OK - doesn't match newlines in the end tag
re.compile(r"""(?i)<!--.*--!?>"""), # $ Alert # NOT OK, does not match newlines [NOT DETECTED]
re.compile(r"""<script.*?>(.|\s)*?<\/script[^>]*>""", re.IGNORECASE), # $ Alert # NOT OK - doesn't match inside the script tag
re.compile(r"""<script[^>]*?>.*?<\/script[^>]*>""", re.IGNORECASE), # $ Alert # NOT OK - doesn't match newlines inside the content
re.compile(r"""<script(\s|\w|=|")*?>.*?<\/script[^>]*>""", re.IGNORECASE | re.DOTALL), # $ Alert # NOT OK - does not match single quotes for attribute values
re.compile(r"""<script(\s|\w|=|')*?>.*?<\/script[^>]*>""", re.IGNORECASE | re.DOTALL), # $ Alert # NOT OK - does not match double quotes for attribute values
re.compile(r"""<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>""", re.IGNORECASE | re.DOTALL), # $ Alert # NOT OK - does not match tabs between attributes
re.compile(r"""<script.*?>.*?<\/script[^>]*>""", re.re.DOTALL), # $ Alert # NOT OK - does not match uppercase SCRIPT tags
re.compile(r"""<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>""", re.DOTALL), # $ Alert # NOT OK - does not match mixed case script tags
re.compile(r"""<script[^>]*?>[\s\S]*?<\/script.*>""", re.IGNORECASE), # $ Alert # NOT OK - doesn't match newlines in the end tag
re.compile(r"""<script[^>]*?>[\s\S]*?<\/script[^>]*?>""", re.IGNORECASE), # OK
re.compile(r"""<script\b[^>]*>([\s\S]*?)<\/script>""", re.IGNORECASE | re.DOTALL), # NOT OK - too strict matching on the end tag
re.compile(r"""<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>"""), #// NOT OK - doesn't match comments with the right capture groups
re.compile(r"""<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"""), # NOT OK - capture groups
re.compile(r"""(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)""", re.IGNORECASE), # NOT OK - capture groups
re.compile(r"""<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))"""), # NOT OK - capture groups
re.compile(r"""<script\b[^>]*>([\s\S]*?)<\/script>""", re.IGNORECASE | re.DOTALL), # $ Alert # NOT OK - too strict matching on the end tag
re.compile(r"""<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>"""), # $ Alert #// NOT OK - doesn't match comments with the right capture groups
re.compile(r"""<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"""), # $ Alert # NOT OK - capture groups
re.compile(r"""(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)""", re.IGNORECASE), # $ Alert # NOT OK - capture groups
re.compile(r"""<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))"""), # $ Alert # NOT OK - capture groups
]
doFilters(filters)

View File

@@ -1,3 +1,8 @@
#select
| LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |
| LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |
| LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |
| LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |
edges
| LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for request | provenance | |
| LogInjectionBad.py:7:19:7:25 | ControlFlowNode for request | LogInjectionBad.py:17:12:17:18 | ControlFlowNode for request | provenance | |
@@ -44,8 +49,3 @@ nodes
| LogInjectionBad.py:35:12:35:35 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
subpaths
#select
| LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:18:21:18:40 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |
| LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:24:18:24:37 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |
| LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:30:25:30:44 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |
| LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | LogInjectionBad.py:37:19:37:38 | ControlFlowNode for BinaryExpr | This log entry depends on a $@. | LogInjectionBad.py:7:19:7:25 | ControlFlowNode for ImportMember | user-provided value |

View File

@@ -1 +1,2 @@
Security/CWE-117/LogInjection.ql
query: Security/CWE-117/LogInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -4,7 +4,7 @@
@Desc Log Injection
"""
from flask import Flask
from flask import request
from flask import request # $ Source
from django.utils.log import request_logger
import logging
@@ -15,26 +15,26 @@ app = Flask(__name__)
@app.route('/bad1')
def bad1():
name = request.args.get('name')
app.logger.info('User name: ' + name) # Bad
app.logger.info('User name: ' + name) # $ Alert # Bad
return 'bad1'
@app.route('/bad2')
def bad2():
name = request.args.get('name')
logging.info('User name: ' + name) # Bad
logging.info('User name: ' + name) # $ Alert # Bad
return 'bad2'
@app.route('/bad3')
def bad3():
name = request.args.get('name')
request_logger.warn('User name: ' + name) # Bad
request_logger.warn('User name: ' + name) # $ Alert # Bad
return 'bad3'
@app.route('/bad4')
def bad4():
name = request.args.get('name')
logtest = logging.getLogger('test')
logtest.debug('User name: ' + name) # Bad
logtest.debug('User name: ' + name) # $ Alert # Bad
return 'bad4'
if __name__ == '__main__':

View File

@@ -1 +1 @@
Security/CWE-209/StackTraceExposure.ql
query: Security/CWE-209/StackTraceExposure.ql

View File

@@ -1 +1,2 @@
Security/CWE-215/FlaskDebug.ql
query: Security/CWE-215/FlaskDebug.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -7,8 +7,8 @@ def main():
raise Exception()
# bad
app.run(debug=True)
app.run('host', 8080, True)
app.run(debug=True) # $ Alert
app.run('host', 8080, True) # $ Alert
# okay
app.run()
@@ -23,11 +23,11 @@ app.notrun(debug=True)
DEBUG = True
app.run(debug=DEBUG) # NOT OK
app.run(debug=DEBUG) # $ Alert # NOT OK
DEBUG = 1
app.run(debug=DEBUG) # NOT OK
app.run(debug=DEBUG) # $ Alert # NOT OK
if False:
app.run(debug=True)
@@ -35,12 +35,12 @@ if False:
runapp = app.run
runapp(debug=True) # NOT OK
runapp(debug=True) # $ Alert # NOT OK
# imports from other module
import settings
app.run(debug=settings.ALWAYS_TRUE) # NOT OK
app.run(debug=settings.ALWAYS_TRUE) # $ Alert # NOT OK
# depending on environment values

View File

@@ -1 +1,2 @@
Security/CWE-285/PamAuthorization.ql
query: Security/CWE-285/PamAuthorization.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,7 +1,7 @@
from ctypes import CDLL, POINTER, Structure, byref
from ctypes import c_char_p, c_int
from ctypes.util import find_library
from flask import Flask, request, redirect
from flask import Flask, request, redirect # $ Source
class PamHandle(Structure):
@@ -73,7 +73,7 @@ def bad():
conv = PamConv(None, 0)
retval = pam_start(service, username, byref(conv), byref(handle))
retval = pam_authenticate(handle, 0)
retval = pam_authenticate(handle, 0) # $ Alert
# NOT OK: no call to `pam_acct_mgmt`
auth_success = retval == 0

View File

@@ -1 +1,2 @@
Security/CWE-295/MissingHostKeyValidation.ql
query: Security/CWE-295/MissingHostKeyValidation.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,19 +2,19 @@ from paramiko.client import AutoAddPolicy, WarningPolicy, RejectPolicy, SSHClien
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy) # bad
client.set_missing_host_key_policy(AutoAddPolicy) # $ Alert # bad
client.set_missing_host_key_policy(RejectPolicy) # good
client.set_missing_host_key_policy(WarningPolicy) # bad
client.set_missing_host_key_policy(WarningPolicy) # $ Alert # bad
# Using instances
client.set_missing_host_key_policy(AutoAddPolicy()) # bad
client.set_missing_host_key_policy(AutoAddPolicy()) # $ Alert # bad
client.set_missing_host_key_policy(RejectPolicy()) # good
client.set_missing_host_key_policy(WarningPolicy()) # bad
client.set_missing_host_key_policy(WarningPolicy()) # $ Alert # bad
# different import
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy) # bad
client.set_missing_host_key_policy(paramiko.AutoAddPolicy) # $ Alert # bad

View File

@@ -1 +1,2 @@
Security/CWE-295/RequestWithoutValidation.ql
query: Security/CWE-295/RequestWithoutValidation.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -2,14 +2,14 @@ import requests
#Simple cases
requests.get('https://semmle.com', verify=True) # GOOD
requests.get('https://semmle.com', verify=False) # BAD
requests.get('https://semmle.com', verify=False) # $ Alert # BAD
requests.post('https://semmle.com', verify=True) # GOOD
requests.post('https://semmle.com', verify=False) # BAD
requests.post('https://semmle.com', verify=False) # $ Alert # BAD
# Simple flow
put = requests.put
put('https://semmle.com', verify="/path/to/cert/") # GOOD
put('https://semmle.com', verify=False) # BAD
put('https://semmle.com', verify=False) # $ Alert # BAD
#Other flow
delete = requests.delete
@@ -25,17 +25,17 @@ def req1(verify=False):
patch = requests.patch
def req2(verify):
patch('https://semmle.com', verify=verify) # BAD (from line 30)
patch('https://semmle.com', verify=verify) # $ Alert # BAD (from line 30)
req2(False) # BAD (at line 28)
req2("/path/to/cert/") # GOOD
#Falsey value
requests.post('https://semmle.com', verify=0) # BAD
requests.post('https://semmle.com', verify=0) # $ Alert # BAD
# requests treat `None` as default value, which means it is turned on
requests.get('https://semmle.com') # OK
requests.get('https://semmle.com', verify=None) # OK
s = requests.Session()
s.get("url", verify=False) # BAD
s.get("url", verify=False) # $ Alert # BAD

View File

@@ -1,3 +1,28 @@
#select
| test.py:20:48:20:55 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:20:48:20:55 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:22:58:22:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:22:58:22:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:23:58:23:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:23:58:23:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:27:40:27:47 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:27:40:27:47 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:30:58:30:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:30:58:30:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:37:11:37:24 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:39:22:39:35 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:40:22:40:35 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:45:11:45:11 | ControlFlowNode for x | test.py:44:9:44:25 | ControlFlowNode for Attribute() | test.py:45:11:45:11 | ControlFlowNode for x | This expression logs $@ as clear text. | test.py:44:9:44:25 | ControlFlowNode for Attribute() | sensitive data (password) |
| test.py:49:15:49:36 | ControlFlowNode for social_security_number | test.py:48:14:48:35 | ControlFlowNode for social_security_number | test.py:49:15:49:36 | ControlFlowNode for social_security_number | This expression logs $@ as clear text. | test.py:48:14:48:35 | ControlFlowNode for social_security_number | sensitive data (private) |
| test.py:50:15:50:17 | ControlFlowNode for ssn | test.py:48:38:48:40 | ControlFlowNode for ssn | test.py:50:15:50:17 | ControlFlowNode for ssn | This expression logs $@ as clear text. | test.py:48:38:48:40 | ControlFlowNode for ssn | sensitive data (private) |
| test.py:52:15:52:24 | ControlFlowNode for passportNo | test.py:48:54:48:63 | ControlFlowNode for passportNo | test.py:52:15:52:24 | ControlFlowNode for passportNo | This expression logs $@ as clear text. | test.py:48:54:48:63 | ControlFlowNode for passportNo | sensitive data (private) |
| test.py:55:15:55:23 | ControlFlowNode for post_code | test.py:54:14:54:22 | ControlFlowNode for post_code | test.py:55:15:55:23 | ControlFlowNode for post_code | This expression logs $@ as clear text. | test.py:54:14:54:22 | ControlFlowNode for post_code | sensitive data (private) |
| test.py:56:15:56:21 | ControlFlowNode for zipCode | test.py:54:25:54:31 | ControlFlowNode for zipCode | test.py:56:15:56:21 | ControlFlowNode for zipCode | This expression logs $@ as clear text. | test.py:54:25:54:31 | ControlFlowNode for zipCode | sensitive data (private) |
| test.py:57:15:57:26 | ControlFlowNode for home_address | test.py:54:34:54:45 | ControlFlowNode for home_address | test.py:57:15:57:26 | ControlFlowNode for home_address | This expression logs $@ as clear text. | test.py:54:34:54:45 | ControlFlowNode for home_address | sensitive data (private) |
| test.py:60:15:60:27 | ControlFlowNode for user_latitude | test.py:59:14:59:26 | ControlFlowNode for user_latitude | test.py:60:15:60:27 | ControlFlowNode for user_latitude | This expression logs $@ as clear text. | test.py:59:14:59:26 | ControlFlowNode for user_latitude | sensitive data (private) |
| test.py:61:15:61:28 | ControlFlowNode for user_longitude | test.py:59:29:59:42 | ControlFlowNode for user_longitude | test.py:61:15:61:28 | ControlFlowNode for user_longitude | This expression logs $@ as clear text. | test.py:59:29:59:42 | ControlFlowNode for user_longitude | sensitive data (private) |
| test.py:64:15:64:27 | ControlFlowNode for mobile_number | test.py:63:14:63:26 | ControlFlowNode for mobile_number | test.py:64:15:64:27 | ControlFlowNode for mobile_number | This expression logs $@ as clear text. | test.py:63:14:63:26 | ControlFlowNode for mobile_number | sensitive data (private) |
| test.py:65:15:65:21 | ControlFlowNode for phoneNo | test.py:63:29:63:35 | ControlFlowNode for phoneNo | test.py:65:15:65:21 | ControlFlowNode for phoneNo | This expression logs $@ as clear text. | test.py:63:29:63:35 | ControlFlowNode for phoneNo | sensitive data (private) |
| test.py:68:15:68:24 | ControlFlowNode for creditcard | test.py:67:14:67:23 | ControlFlowNode for creditcard | test.py:68:15:68:24 | ControlFlowNode for creditcard | This expression logs $@ as clear text. | test.py:67:14:67:23 | ControlFlowNode for creditcard | sensitive data (private) |
| test.py:69:15:69:24 | ControlFlowNode for debit_card | test.py:67:26:67:35 | ControlFlowNode for debit_card | test.py:69:15:69:24 | ControlFlowNode for debit_card | This expression logs $@ as clear text. | test.py:67:26:67:35 | ControlFlowNode for debit_card | sensitive data (private) |
| test.py:70:15:70:25 | ControlFlowNode for bank_number | test.py:67:38:67:48 | ControlFlowNode for bank_number | test.py:70:15:70:25 | ControlFlowNode for bank_number | This expression logs $@ as clear text. | test.py:67:38:67:48 | ControlFlowNode for bank_number | sensitive data (private) |
| test.py:73:15:73:17 | ControlFlowNode for ccn | test.py:67:76:67:78 | ControlFlowNode for ccn | test.py:73:15:73:17 | ControlFlowNode for ccn | This expression logs $@ as clear text. | test.py:67:76:67:78 | ControlFlowNode for ccn | sensitive data (private) |
| test.py:74:15:74:22 | ControlFlowNode for user_ccn | test.py:67:81:67:88 | ControlFlowNode for user_ccn | test.py:74:15:74:22 | ControlFlowNode for user_ccn | This expression logs $@ as clear text. | test.py:67:81:67:88 | ControlFlowNode for user_ccn | sensitive data (private) |
edges
| test.py:19:5:19:12 | ControlFlowNode for password | test.py:20:48:20:55 | ControlFlowNode for password | provenance | |
| test.py:19:5:19:12 | ControlFlowNode for password | test.py:22:58:22:65 | ControlFlowNode for password | provenance | |
@@ -67,28 +92,3 @@ nodes
| test.py:73:15:73:17 | ControlFlowNode for ccn | semmle.label | ControlFlowNode for ccn |
| test.py:74:15:74:22 | ControlFlowNode for user_ccn | semmle.label | ControlFlowNode for user_ccn |
subpaths
#select
| test.py:20:48:20:55 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:20:48:20:55 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:22:58:22:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:22:58:22:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:23:58:23:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:23:58:23:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:27:40:27:47 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:27:40:27:47 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:30:58:30:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:30:58:30:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:37:11:37:24 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:39:22:39:35 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:40:22:40:35 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:45:11:45:11 | ControlFlowNode for x | test.py:44:9:44:25 | ControlFlowNode for Attribute() | test.py:45:11:45:11 | ControlFlowNode for x | This expression logs $@ as clear text. | test.py:44:9:44:25 | ControlFlowNode for Attribute() | sensitive data (password) |
| test.py:49:15:49:36 | ControlFlowNode for social_security_number | test.py:48:14:48:35 | ControlFlowNode for social_security_number | test.py:49:15:49:36 | ControlFlowNode for social_security_number | This expression logs $@ as clear text. | test.py:48:14:48:35 | ControlFlowNode for social_security_number | sensitive data (private) |
| test.py:50:15:50:17 | ControlFlowNode for ssn | test.py:48:38:48:40 | ControlFlowNode for ssn | test.py:50:15:50:17 | ControlFlowNode for ssn | This expression logs $@ as clear text. | test.py:48:38:48:40 | ControlFlowNode for ssn | sensitive data (private) |
| test.py:52:15:52:24 | ControlFlowNode for passportNo | test.py:48:54:48:63 | ControlFlowNode for passportNo | test.py:52:15:52:24 | ControlFlowNode for passportNo | This expression logs $@ as clear text. | test.py:48:54:48:63 | ControlFlowNode for passportNo | sensitive data (private) |
| test.py:55:15:55:23 | ControlFlowNode for post_code | test.py:54:14:54:22 | ControlFlowNode for post_code | test.py:55:15:55:23 | ControlFlowNode for post_code | This expression logs $@ as clear text. | test.py:54:14:54:22 | ControlFlowNode for post_code | sensitive data (private) |
| test.py:56:15:56:21 | ControlFlowNode for zipCode | test.py:54:25:54:31 | ControlFlowNode for zipCode | test.py:56:15:56:21 | ControlFlowNode for zipCode | This expression logs $@ as clear text. | test.py:54:25:54:31 | ControlFlowNode for zipCode | sensitive data (private) |
| test.py:57:15:57:26 | ControlFlowNode for home_address | test.py:54:34:54:45 | ControlFlowNode for home_address | test.py:57:15:57:26 | ControlFlowNode for home_address | This expression logs $@ as clear text. | test.py:54:34:54:45 | ControlFlowNode for home_address | sensitive data (private) |
| test.py:60:15:60:27 | ControlFlowNode for user_latitude | test.py:59:14:59:26 | ControlFlowNode for user_latitude | test.py:60:15:60:27 | ControlFlowNode for user_latitude | This expression logs $@ as clear text. | test.py:59:14:59:26 | ControlFlowNode for user_latitude | sensitive data (private) |
| test.py:61:15:61:28 | ControlFlowNode for user_longitude | test.py:59:29:59:42 | ControlFlowNode for user_longitude | test.py:61:15:61:28 | ControlFlowNode for user_longitude | This expression logs $@ as clear text. | test.py:59:29:59:42 | ControlFlowNode for user_longitude | sensitive data (private) |
| test.py:64:15:64:27 | ControlFlowNode for mobile_number | test.py:63:14:63:26 | ControlFlowNode for mobile_number | test.py:64:15:64:27 | ControlFlowNode for mobile_number | This expression logs $@ as clear text. | test.py:63:14:63:26 | ControlFlowNode for mobile_number | sensitive data (private) |
| test.py:65:15:65:21 | ControlFlowNode for phoneNo | test.py:63:29:63:35 | ControlFlowNode for phoneNo | test.py:65:15:65:21 | ControlFlowNode for phoneNo | This expression logs $@ as clear text. | test.py:63:29:63:35 | ControlFlowNode for phoneNo | sensitive data (private) |
| test.py:68:15:68:24 | ControlFlowNode for creditcard | test.py:67:14:67:23 | ControlFlowNode for creditcard | test.py:68:15:68:24 | ControlFlowNode for creditcard | This expression logs $@ as clear text. | test.py:67:14:67:23 | ControlFlowNode for creditcard | sensitive data (private) |
| test.py:69:15:69:24 | ControlFlowNode for debit_card | test.py:67:26:67:35 | ControlFlowNode for debit_card | test.py:69:15:69:24 | ControlFlowNode for debit_card | This expression logs $@ as clear text. | test.py:67:26:67:35 | ControlFlowNode for debit_card | sensitive data (private) |
| test.py:70:15:70:25 | ControlFlowNode for bank_number | test.py:67:38:67:48 | ControlFlowNode for bank_number | test.py:70:15:70:25 | ControlFlowNode for bank_number | This expression logs $@ as clear text. | test.py:67:38:67:48 | ControlFlowNode for bank_number | sensitive data (private) |
| test.py:73:15:73:17 | ControlFlowNode for ccn | test.py:67:76:67:78 | ControlFlowNode for ccn | test.py:73:15:73:17 | ControlFlowNode for ccn | This expression logs $@ as clear text. | test.py:67:76:67:78 | ControlFlowNode for ccn | sensitive data (private) |
| test.py:74:15:74:22 | ControlFlowNode for user_ccn | test.py:67:81:67:88 | ControlFlowNode for user_ccn | test.py:74:15:74:22 | ControlFlowNode for user_ccn | This expression logs $@ as clear text. | test.py:67:81:67:88 | ControlFlowNode for user_ccn | sensitive data (private) |

View File

@@ -1 +1,2 @@
Security/CWE-312/CleartextLogging.ql
query: Security/CWE-312/CleartextLogging.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -16,62 +16,62 @@ def get_cert():
def log_password():
password = get_password()
logging.info("logging.info Password '%s'", password) # NOT OK
password = get_password() # $ Source
logging.info("logging.info Password '%s'", password) # $ Alert # NOT OK
LOGGER.log(logging.INFO, "LOGGER.log Password '%s'", password) # NOT OK
logging.root.info("logging.root.info Password '%s'", password) # NOT OK
LOGGER.log(logging.INFO, "LOGGER.log Password '%s'", password) # $ Alert # NOT OK
logging.root.info("logging.root.info Password '%s'", password) # $ Alert # NOT OK
# name of logger variable should not matter
foo = LOGGER
foo.info("foo.info Password '%s'", password) # NOT OK
foo.info("foo.info Password '%s'", password) # $ Alert # NOT OK
# return value from function
get_logger().info("get_logger().info Password '%s'", password) # NOT OK
get_logger().info("get_logger().info Password '%s'", password) # $ Alert # NOT OK
def log_cert():
logging.debug("Cert=%s", get_cert()) # OK
def print_password():
print(get_password()) # NOT OK
print(get_password()) # $ Alert # NOT OK
sys.stdout.write(get_password()) # NOT OK
sys.stderr.write(get_password()) # NOT OK
sys.stdout.write(get_password()) # $ Alert # NOT OK
sys.stderr.write(get_password()) # $ Alert # NOT OK
import getpass
x = getpass.getpass()
print(x) # NOT OK
x = getpass.getpass() # $ Source
print(x) # $ Alert # NOT OK
def log_private():
def log1(social_security_number, ssn, className, passportNo):
print(social_security_number) # NOT OK
print(ssn) # NOT OK
def log1(social_security_number, ssn, className, passportNo): # $ Source
print(social_security_number) # $ Alert # NOT OK
print(ssn) # $ Alert # NOT OK
print(className) # OK
print(passportNo) # NOT OK
print(passportNo) # $ Alert # NOT OK
def log2(post_code, zipCode, home_address):
print(post_code) # NOT OK
print(zipCode) # NOT OK
print(home_address) # NOT OK
def log2(post_code, zipCode, home_address): # $ Source
print(post_code) # $ Alert # NOT OK
print(zipCode) # $ Alert # NOT OK
print(home_address) # $ Alert # NOT OK
def log3(user_latitude, user_longitude):
print(user_latitude) # NOT OK
print(user_longitude) # NOT OK
def log3(user_latitude, user_longitude): # $ Source
print(user_latitude) # $ Alert # NOT OK
print(user_longitude) # $ Alert # NOT OK
def log4(mobile_number, phoneNo):
print(mobile_number) # NOT OK
print(phoneNo) # NOT OK
def log4(mobile_number, phoneNo): # $ Source
print(mobile_number) # $ Alert # NOT OK
print(phoneNo) # $ Alert # NOT OK
def log5(creditcard, debit_card, bank_number, bank_account, accountNo, ccn, user_ccn, succNode):
print(creditcard) # NOT OK
print(debit_card) # NOT OK
print(bank_number) # NOT OK
def log5(creditcard, debit_card, bank_number, bank_account, accountNo, ccn, user_ccn, succNode): # $ Source
print(creditcard) # $ Alert # NOT OK
print(debit_card) # $ Alert # NOT OK
print(bank_number) # $ Alert # NOT OK
print(bank_account) # NOT OK, but NOT FOUND - "account" is treated as having the "id" classification and thus excluded.
print(accountNo) # NOT OK, but NOT FOUND - "account" is treated as having the "id" classification and thus excluded.
print(ccn) # NOT OK
print(user_ccn) # NOT OK
print(ccn) # $ Alert # NOT OK
print(user_ccn) # $ Alert # NOT OK
print(succNode) # OK

View File

@@ -1,3 +1,7 @@
#select
| test.py:12:21:12:28 | ControlFlowNode for password | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:12:21:12:28 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:13:22:13:45 | ControlFlowNode for Attribute() | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:13:22:13:45 | ControlFlowNode for Attribute() | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:15:26:15:33 | ControlFlowNode for password | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:15:26:15:33 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
edges
| test.py:9:5:9:12 | ControlFlowNode for password | test.py:12:21:12:28 | ControlFlowNode for password | provenance | |
| test.py:9:5:9:12 | ControlFlowNode for password | test.py:13:22:13:45 | ControlFlowNode for Attribute() | provenance | |
@@ -10,7 +14,3 @@ nodes
| test.py:13:22:13:45 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:15:26:15:33 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
subpaths
#select
| test.py:12:21:12:28 | ControlFlowNode for password | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:12:21:12:28 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:13:22:13:45 | ControlFlowNode for Attribute() | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:13:22:13:45 | ControlFlowNode for Attribute() | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:15:26:15:33 | ControlFlowNode for password | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:15:26:15:33 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |

View File

@@ -1 +1,2 @@
Security/CWE-312/CleartextStorage.ql
query: Security/CWE-312/CleartextStorage.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -6,10 +6,10 @@ def get_password():
def write_password(filename):
password = get_password()
password = get_password() # $ Source
path = pathlib.Path(filename)
path.write_text(password) # NOT OK
path.write_bytes(password.encode("utf-8")) # NOT OK
path.write_text(password) # $ Alert # NOT OK
path.write_bytes(password.encode("utf-8")) # $ Alert # NOT OK
path.open("w").write(password) # NOT OK
path.open("w").write(password) # $ Alert # NOT OK

View File

@@ -1,3 +1,8 @@
#select
| password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | This expression stores $@ as clear text. | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | sensitive data (password) |
| password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | This expression stores $@ as clear text. | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | sensitive data (password) |
| test.py:17:20:17:27 | ControlFlowNode for password | test.py:15:16:15:29 | ControlFlowNode for get_password() | test.py:17:20:17:27 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:15:16:15:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:19:25:19:29 | ControlFlowNode for lines | test.py:15:16:15:29 | ControlFlowNode for get_password() | test.py:19:25:19:29 | ControlFlowNode for lines | This expression stores $@ as clear text. | test.py:15:16:15:29 | ControlFlowNode for get_password() | sensitive data (password) |
edges
| password_in_cookie.py:7:5:7:12 | ControlFlowNode for password | password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | provenance | |
| password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | password_in_cookie.py:7:5:7:12 | ControlFlowNode for password | provenance | |
@@ -24,8 +29,3 @@ nodes
| test.py:18:18:18:32 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
| test.py:19:25:19:29 | ControlFlowNode for lines | semmle.label | ControlFlowNode for lines |
subpaths
#select
| password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | This expression stores $@ as clear text. | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | sensitive data (password) |
| password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | This expression stores $@ as clear text. | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | sensitive data (password) |
| test.py:17:20:17:27 | ControlFlowNode for password | test.py:15:16:15:29 | ControlFlowNode for get_password() | test.py:17:20:17:27 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:15:16:15:29 | ControlFlowNode for get_password() | sensitive data (password) |
| test.py:19:25:19:29 | ControlFlowNode for lines | test.py:15:16:15:29 | ControlFlowNode for get_password() | test.py:19:25:19:29 | ControlFlowNode for lines | This expression stores $@ as clear text. | test.py:15:16:15:29 | ControlFlowNode for get_password() | sensitive data (password) |

View File

@@ -1 +1,2 @@
Security/CWE-312/CleartextStorage.ql
query: Security/CWE-312/CleartextStorage.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -4,14 +4,14 @@ app = Flask("Leak password")
@app.route('/')
def index():
password = request.args.get("password")
password = request.args.get("password") # $ Source
resp = make_response(render_template(...))
resp.set_cookie("password", password) # NOT OK
resp.set_cookie("password", password) # $ Alert # NOT OK
return resp
@app.route('/')
def index2():
password = request.args.get("password")
password = request.args.get("password") # $ Source
resp = Response(...)
resp.set_cookie("password", password) # NOT OK
resp.set_cookie("password", password) # $ Alert # NOT OK
return resp

View File

@@ -12,11 +12,11 @@ def write_cert(filename):
file.writelines(lines) # OK
def write_password(filename):
password = get_password()
password = get_password() # $ Source
with open(filename, "w") as file:
file.write(password) # NOT OK
file.write(password) # $ Alert # NOT OK
lines = [password + "\n"]
file.writelines(lines) # NOT OK
file.writelines(lines) # $ Alert # NOT OK
def FPs():
# just like for cleartext-logging see that file for more elaborate tests

View File

@@ -1 +1,2 @@
Security/CWE-326/WeakCryptoKey.ql
query: Security/CWE-326/WeakCryptoKey.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -65,23 +65,23 @@ RSA.generate(RSA_STRONG)
# Weak keys
dsa_gen_key(DSA_WEAK)
ec_gen_key(EC_WEAK)
rsa_gen_key(65537, RSA_WEAK)
dsa_gen_key(DSA_WEAK) # $ Alert
ec_gen_key(EC_WEAK) # $ Alert
rsa_gen_key(65537, RSA_WEAK) # $ Alert
dsa_gen_key(key_size=DSA_WEAK)
ec_gen_key(curve=EC_WEAK)
rsa_gen_key(65537, key_size=RSA_WEAK)
dsa_gen_key(key_size=DSA_WEAK) # $ Alert
ec_gen_key(curve=EC_WEAK) # $ Alert
rsa_gen_key(65537, key_size=RSA_WEAK) # $ Alert
DSA.generate(DSA_WEAK)
RSA.generate(RSA_WEAK)
DSA.generate(DSA_WEAK) # $ Alert
RSA.generate(RSA_WEAK) # $ Alert
# ------------------------------------------------------------------------------
# Through function calls
def make_new_rsa_key_weak(bits):
return RSA.generate(bits) # NOT OK
return RSA.generate(bits) # $ Alert # NOT OK
make_new_rsa_key_weak(RSA_WEAK)

View File

@@ -1 +1,2 @@
Security/CWE-327/BrokenCryptoAlgorithm.ql
query: Security/CWE-327/BrokenCryptoAlgorithm.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -8,11 +8,11 @@ key = os.urandom(256//8)
secret_message = b"secret message"
cipher = ARC4.new(key)
encrypted = cipher.encrypt(secret_message) # NOT OK
encrypted = cipher.encrypt(secret_message) # $ Alert # NOT OK
print(secret_message, encrypted)
cipher = AES.new(key, AES.MODE_ECB)
encrypted = cipher.encrypt(secret_message) # NOT OK
encrypted = cipher.encrypt(secret_message) # $ Alert # NOT OK
print(secret_message, encrypted)

View File

@@ -10,7 +10,7 @@ cipher = Cipher(algorithm, mode=None)
secret_message = b"secret message"
encryptor = cipher.encryptor()
encrypted = encryptor.update(secret_message) # NOT OK
encrypted = encryptor.update(secret_message) # $ Alert # NOT OK
encrypted += encryptor.finalize()
print(secret_message, encrypted)
@@ -19,7 +19,7 @@ algorithm = algorithms.AES(key)
cipher = Cipher(algorithm, mode=modes.ECB())
encryptor = cipher.encryptor()
encrypted = encryptor.update(secret_message + b'\x80\x00') # NOT OK
encrypted = encryptor.update(secret_message + b'\x80\x00') # $ Alert # NOT OK
encrypted += encryptor.finalize()
print(secret_message, encrypted)

View File

@@ -1 +1,2 @@
Security/CWE-327/InsecureDefaultProtocol.ql
query: Security/CWE-327/InsecureDefaultProtocol.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -4,4 +4,4 @@ import ssl
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1_2)
# possibly insecure default
ssl.wrap_socket()
ssl.wrap_socket() # $ Alert

View File

@@ -3,25 +3,25 @@ from OpenSSL import SSL
from ssl import SSLContext
# insecure versions specified
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv2)
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv3)
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1)
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv2) # $ Alert
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv3) # $ Alert
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1) # $ Alert
SSLContext(protocol=ssl.PROTOCOL_SSLv2)
SSLContext(protocol=ssl.PROTOCOL_SSLv3)
SSLContext(protocol=ssl.PROTOCOL_TLSv1)
SSLContext(protocol=ssl.PROTOCOL_SSLv2) # $ Alert
SSLContext(protocol=ssl.PROTOCOL_SSLv3) # $ Alert
SSLContext(protocol=ssl.PROTOCOL_TLSv1) # $ Alert
SSL.Context(SSL.SSLv2_METHOD)
SSL.Context(SSL.SSLv3_METHOD)
SSL.Context(SSL.TLSv1_METHOD)
SSL.Context(SSL.SSLv2_METHOD) # $ Alert
SSL.Context(SSL.SSLv3_METHOD) # $ Alert
SSL.Context(SSL.TLSv1_METHOD) # $ Alert
METHOD = SSL.SSLv2_METHOD
SSL.Context(METHOD)
SSL.Context(METHOD) # $ Alert
# importing the protocol constant directly
from ssl import PROTOCOL_SSLv2
ssl.wrap_socket(ssl_version=PROTOCOL_SSLv2)
SSLContext(protocol=PROTOCOL_SSLv2)
ssl.wrap_socket(ssl_version=PROTOCOL_SSLv2) # $ Alert
SSLContext(protocol=PROTOCOL_SSLv2) # $ Alert
# secure versions specified
ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1_2)

View File

@@ -1 +1,2 @@
Security/CWE-327/InsecureProtocol.ql
query: Security/CWE-327/InsecureProtocol.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -22,9 +22,9 @@ with socket.create_connection((hostname, 443)) as sock:
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with copy_completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
with copy_completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with copy_also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
with copy_also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())

View File

@@ -10,9 +10,9 @@ with socket.create_connection((hostname, 443)) as sock:
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
with completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
with socket.create_connection((hostname, 443)) as sock:
with also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
with also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())

View File

@@ -5,7 +5,7 @@ def test_fluent():
hostname = 'www.python.org'
context = SSL.Context(SSL.SSLv23_METHOD)
conn = SSL.Connection(context, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
conn = SSL.Connection(context, socket.socket(socket.AF_INET, socket.SOCK_STREAM)) # $ Alert
r = conn.connect((hostname, 443))
print(conn.get_protocol_version_name())
@@ -15,7 +15,7 @@ def test_fluent_no_TLSv1():
context = SSL.Context(SSL.SSLv23_METHOD)
context.set_options(SSL.OP_NO_TLSv1)
conn = SSL.Connection(context, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
conn = SSL.Connection(context, socket.socket(socket.AF_INET, socket.SOCK_STREAM)) # $ Alert
r = conn.connect((hostname, 443))
print(conn.get_protocol_version_name())

View File

@@ -6,7 +6,7 @@ def test_fluent_tls():
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
@@ -16,7 +16,7 @@ def test_fluent_tls_no_TLSv1():
context.options |= ssl.OP_NO_TLSv1
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
def test_fluent_tls_client_no_TLSv1():
@@ -25,7 +25,7 @@ def test_fluent_tls_client_no_TLSv1():
context.options |= ssl.OP_NO_TLSv1
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
def test_fluent_tls_server_no_TLSv1():
@@ -34,7 +34,7 @@ def test_fluent_tls_server_no_TLSv1():
context.options |= ssl.OP_NO_TLSv1
with socket.create_server((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
def test_fluent_tls_safe():
@@ -54,7 +54,7 @@ def test_fluent_ssl():
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
@@ -68,13 +68,13 @@ def create_secure_context():
def create_connection(context):
with socket.create_connection(('www.python.org', 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
def test_delegated_context_unsafe():
context = create_relaxed_context()
with socket.create_connection(('www.python.org', 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
def test_delegated_context_safe():
@@ -94,7 +94,7 @@ def test_delegated_context_made_unsafe():
context = create_secure_context()
context.options &= ~ssl.OP_NO_TLSv1_1
with socket.create_connection(('www.python.org', 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
def test_delegated_connection_unsafe():
@@ -143,7 +143,7 @@ def test_fluent_ssl_unsafe_version():
context.minimum_version = ssl.TLSVersion.TLSv1_1
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())
def test_fluent_ssl_safe_version():
@@ -162,5 +162,5 @@ def test_fluent_explicitly_unsafe():
context.options &= ~ssl.OP_NO_SSLv3
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock: # $ Alert
print(ssock.version())

View File

@@ -1,3 +1,16 @@
#select
| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | test_cryptodome.py:2:37:2:51 | ControlFlowNode for ImportMember | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptodome.py:2:37:2:51 | ControlFlowNode for ImportMember | Sensitive data (certificate) |
| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) |
| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | Sensitive data (password) |
| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | Sensitive data (password) |
| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | test_cryptography.py:3:37:3:51 | ControlFlowNode for ImportMember | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptography.py:3:37:3:51 | ControlFlowNode for ImportMember | Sensitive data (certificate) |
| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) |
| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | Sensitive data (password) |
| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | Sensitive data (password) |
edges
| test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | test_cryptodome.py:2:23:2:34 | ControlFlowNode for get_password | provenance | |
| test_cryptodome.py:2:23:2:34 | ControlFlowNode for get_password | test_cryptodome.py:13:17:13:28 | ControlFlowNode for get_password | provenance | |
@@ -61,16 +74,3 @@ nodes
| test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous |
subpaths
#select
| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | test_cryptodome.py:2:37:2:51 | ControlFlowNode for ImportMember | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptodome.py:2:37:2:51 | ControlFlowNode for ImportMember | Sensitive data (certificate) |
| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) |
| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | Sensitive data (password) |
| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:2:23:2:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | Sensitive data (password) |
| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | test_cryptography.py:3:37:3:51 | ControlFlowNode for ImportMember | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptography.py:3:37:3:51 | ControlFlowNode for ImportMember | Sensitive data (certificate) |
| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) |
| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | Sensitive data (password) |
| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:3:23:3:34 | ControlFlowNode for ImportMember | Sensitive data (password) |
| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | Sensitive data (password) |

View File

@@ -1 +1,2 @@
Security/CWE-327/WeakSensitiveDataHashing.ql
query: Security/CWE-327/WeakSensitiveDataHashing.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,25 +1,25 @@
from Cryptodome.Hash import MD5, SHA256
from my_module import get_password, get_certificate
from my_module import get_password, get_certificate # $ Source
def get_badly_hashed_certificate():
dangerous = get_certificate()
dangerous = get_certificate() # $ Source
hasher = MD5.new()
hasher.update(dangerous) # NOT OK
hasher.update(dangerous) # $ Alert # NOT OK
return hasher.hexdigest()
def get_badly_hashed_password():
dangerous = get_password()
dangerous = get_password() # $ Source
hasher = MD5.new()
hasher.update(dangerous) # NOT OK
hasher.update(dangerous) # $ Alert # NOT OK
return hasher.hexdigest()
def get_badly_hashed_password2():
dangerous = get_password()
dangerous = get_password() # $ Source
# Although SHA-256 is a strong cryptographic hash functions,
# it is not suitable for password hashing.
hasher = SHA256.new()
hasher.update(dangerous) # NOT OK
hasher.update(dangerous) # $ Alert # NOT OK
return hasher.hexdigest()

View File

@@ -1,29 +1,29 @@
from cryptography.hazmat.primitives import hashes
from binascii import hexlify
from my_module import get_password, get_certificate
from my_module import get_password, get_certificate # $ Source
def get_badly_hashed_certificate():
dangerous = get_certificate()
dangerous = get_certificate() # $ Source
hasher = hashes.Hash(hashes.MD5())
hasher.update(dangerous) # NOT OK
hasher.update(dangerous) # $ Alert # NOT OK
digest = hasher.finalize()
return hexlify(digest).decode("utf-8")
def get_badly_hashed_password():
dangerous = get_password()
dangerous = get_password() # $ Source
hasher = hashes.Hash(hashes.MD5())
hasher.update(dangerous) # NOT OK
hasher.update(dangerous) # $ Alert # NOT OK
digest = hasher.finalize()
return hexlify(digest).decode("utf-8")
def get_badly_hashed_password2():
dangerous = get_password()
dangerous = get_password() # $ Source
# Although SHA-256 is a strong cryptographic hash functions,
# it is not suitable for password hashing.
hasher = hashes.Hash(hashes.SHA256())
hasher.update(dangerous) # NOT OK
hasher.update(dangerous) # $ Alert # NOT OK
digest = hasher.finalize()
return hexlify(digest).decode("utf-8")

View File

@@ -2,19 +2,19 @@ from tempfile import mktemp
import os
def write_results1(results):
filename = mktemp()
filename = mktemp() # $ Alert
with open(filename, "w+") as f:
f.write(results)
print("Results written to", filename)
def write_results2(results):
filename = os.tempnam()
filename = os.tempnam() # $ Alert
with open(filename, "w+") as f:
f.write(results)
print("Results written to", filename)
def write_results3(results):
filename = os.tmpnam()
filename = os.tmpnam() # $ Alert
with open(filename, "w+") as f:
f.write(results)
print("Results written to", filename)

View File

@@ -1 +1,2 @@
Security/CWE-377/InsecureTemporaryFile.ql
query: Security/CWE-377/InsecureTemporaryFile.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,3 +1,9 @@
#select
| unsafe_deserialization.py:15:18:15:24 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:15:18:15:24 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:16:15:16:21 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:16:15:16:21 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:18:19:18:25 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:18:19:18:25 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:21:16:21:22 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:21:16:21:22 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:24:24:24:30 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:24:24:24:30 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
edges
| unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for request | provenance | |
| unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for request | unsafe_deserialization.py:14:15:14:21 | ControlFlowNode for request | provenance | |
@@ -22,9 +28,3 @@ nodes
| unsafe_deserialization.py:21:16:21:22 | ControlFlowNode for payload | semmle.label | ControlFlowNode for payload |
| unsafe_deserialization.py:24:24:24:30 | ControlFlowNode for payload | semmle.label | ControlFlowNode for payload |
subpaths
#select
| unsafe_deserialization.py:15:18:15:24 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:15:18:15:24 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:16:15:16:21 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:16:15:16:21 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:18:19:18:25 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:18:19:18:25 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:21:16:21:22 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:21:16:21:22 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |
| unsafe_deserialization.py:24:24:24:30 | ControlFlowNode for payload | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | unsafe_deserialization.py:24:24:24:30 | ControlFlowNode for payload | Unsafe deserialization depends on a $@. | unsafe_deserialization.py:8:26:8:32 | ControlFlowNode for ImportMember | user-provided value |

View File

@@ -1 +1,2 @@
Security/CWE-502/UnsafeDeserialization.ql
query: Security/CWE-502/UnsafeDeserialization.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -5,20 +5,20 @@ import marshal
from yaml import SafeLoader
from flask import Flask, request
from flask import Flask, request # $ Source
app = Flask(__name__)
@app.route("/")
def hello():
payload = request.args.get("payload")
pickle.loads(payload) # NOT OK
yaml.load(payload) # NOT OK
pickle.loads(payload) # $ Alert # NOT OK
yaml.load(payload) # $ Alert # NOT OK
yaml.load(payload, Loader=SafeLoader) # OK
marshal.loads(payload) # NOT OK
marshal.loads(payload) # $ Alert # NOT OK
import dill
dill.loads(payload) # NOT OK
dill.loads(payload) # $ Alert # NOT OK
import pandas
pandas.read_pickle(payload) # NOT OK
pandas.read_pickle(payload) # $ Alert # NOT OK

View File

@@ -1,3 +1,16 @@
#select
| test.py:8:21:8:26 | ControlFlowNode for target | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:8:21:8:26 | ControlFlowNode for target | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:32:21:32:24 | ControlFlowNode for safe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:32:21:32:24 | ControlFlowNode for safe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:39:21:39:24 | ControlFlowNode for safe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:39:21:39:24 | ControlFlowNode for safe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:46:21:46:24 | ControlFlowNode for safe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:46:21:46:24 | ControlFlowNode for safe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:62:21:62:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:62:21:62:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:69:21:69:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:69:21:69:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:76:21:76:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:76:21:76:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:83:21:83:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:83:21:83:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:93:18:93:26 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:93:18:93:26 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:114:25:114:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:114:25:114:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:140:25:140:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:140:25:140:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:148:25:148:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:148:25:148:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
edges
| test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:1:26:1:32 | ControlFlowNode for request | provenance | |
| test.py:1:26:1:32 | ControlFlowNode for request | test.py:7:14:7:20 | ControlFlowNode for request | provenance | |
@@ -138,16 +151,3 @@ nodes
| test.py:145:17:145:46 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| test.py:148:25:148:33 | ControlFlowNode for untrusted | semmle.label | ControlFlowNode for untrusted |
subpaths
#select
| test.py:8:21:8:26 | ControlFlowNode for target | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:8:21:8:26 | ControlFlowNode for target | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:32:21:32:24 | ControlFlowNode for safe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:32:21:32:24 | ControlFlowNode for safe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:39:21:39:24 | ControlFlowNode for safe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:39:21:39:24 | ControlFlowNode for safe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:46:21:46:24 | ControlFlowNode for safe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:46:21:46:24 | ControlFlowNode for safe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:62:21:62:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:62:21:62:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:69:21:69:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:69:21:69:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:76:21:76:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:76:21:76:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:83:21:83:26 | ControlFlowNode for unsafe | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:83:21:83:26 | ControlFlowNode for unsafe | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:93:18:93:26 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:93:18:93:26 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:114:25:114:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:114:25:114:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:140:25:140:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:140:25:140:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:148:25:148:33 | ControlFlowNode for untrusted | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:148:25:148:33 | ControlFlowNode for untrusted | Untrusted URL redirection depends on a $@. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value |

View File

@@ -1,2 +1,2 @@
Security/CWE-601/UrlRedirect.ql
query: Security/CWE-601/UrlRedirect.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,11 +1,11 @@
from flask import Flask, request, redirect
from flask import Flask, request, redirect # $ Source
app = Flask(__name__)
@app.route('/')
def hello():
target = request.args.get('target', '')
return redirect(target, code=302)
return redirect(target, code=302) # $ Alert
# Check for safe prefixes
@@ -29,21 +29,21 @@ def ok2():
def ok3():
untrusted = request.args.get('target', '')
safe = "https://safe.com/{}".format(untrusted)
return redirect(safe, code=302) # FP
return redirect(safe, code=302) # $ SPURIOUS: Alert # FP
@app.route('/ok4')
def ok4():
untrusted = request.args.get('target', '')
safe = f"https://safe.com/{untrusted}"
return redirect(safe, code=302) # FP
return redirect(safe, code=302) # $ SPURIOUS: Alert # FP
@app.route('/ok5')
def ok5():
untrusted = request.args.get('target', '')
safe = "https://safe.com/%s" % untrusted
return redirect(safe, code=302) # FP
return redirect(safe, code=302) # $ SPURIOUS: Alert # FP
@app.route('/const-str-compare')
@@ -59,41 +59,41 @@ def const_str_compare():
def not_ok1():
untrusted = request.args.get('target', '')
unsafe = untrusted + "?login=success"
return redirect(unsafe, code=302)
return redirect(unsafe, code=302) # $ Alert
@app.route('/not_ok2')
def not_ok2():
untrusted = request.args.get('target', '')
unsafe = "{}?login=success".format(untrusted)
return redirect(unsafe, code=302)
return redirect(unsafe, code=302) # $ Alert
@app.route('/not_ok3')
def not_ok3():
untrusted = request.args.get('target', '')
unsafe = f"{untrusted}?login=success"
return redirect(unsafe, code=302)
return redirect(unsafe, code=302) # $ Alert
@app.route('/not_ok4')
def not_ok4():
untrusted = request.args.get('target', '')
unsafe = "%s?login=success" % untrusted
return redirect(unsafe, code=302)
return redirect(unsafe, code=302) # $ Alert
from django.utils.http import url_has_allowed_host_and_scheme
import math
import math
@app.route('/ok6')
def ok6():
untrusted = request.args.get('target', '')
# random chance.
# random chance.
if math.random() > 0.5:
redirect(untrusted, code=302) # NOT OK
redirect(untrusted, code=302) # $ Alert # NOT OK
if url_has_allowed_host_and_scheme(untrusted, allowed_hosts=None):
return redirect(untrusted, code=302) # OK
return redirect("https://example.com", code=302) # OK
import yarl
@@ -111,7 +111,7 @@ def not_ok5():
untrusted = request.args.get('target', '')
# no backslash replace
if not yarl.URL(untrusted).is_absolute():
return redirect(untrusted, code=302) # NOT OK
return redirect(untrusted, code=302) # $ Alert # NOT OK
return redirect("/", code=302)
from urllib.parse import urlparse
@@ -137,7 +137,7 @@ def not_ok6():
untrusted = request.args.get('target', '')
# no backslash replace
if not urlparse(untrusted).netloc:
return redirect(untrusted, code=302) # NOT OK
return redirect(untrusted, code=302) # $ Alert # NOT OK
return redirect("/", code=302)
@app.route('/not_ok7')
@@ -145,7 +145,7 @@ def not_ok7():
untrusted = request.args.get('target', '')
# wrong check
if urlparse(untrusted).netloc != "":
return redirect(untrusted, code=302) # NOT OK
return redirect(untrusted, code=302) # $ Alert # NOT OK
return redirect("/", code=302)
@app.route('/ok10')

View File

@@ -1 +1,2 @@
Security/CWE-611/Xxe.ql
query: Security/CWE-611/Xxe.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,4 +1,4 @@
from flask import Flask, request
from flask import Flask, request # $ Source
import lxml.etree
import markupsafe
@@ -7,7 +7,7 @@ app = Flask(__name__)
@app.route("/vuln-handler")
def vuln_handler():
xml_content = request.args['xml_content']
return lxml.etree.fromstring(xml_content).text
return lxml.etree.fromstring(xml_content).text # $ Alert
@app.route("/safe-handler")
def safe_handler():
@@ -28,7 +28,7 @@ def super_vuln_handler():
# allows DoS attacks
huge_tree=True,
)
return lxml.etree.fromstring(xml_content, parser=parser).text
return lxml.etree.fromstring(xml_content, parser=parser).text # $ Alert
@app.route("/sanitized-handler")
def sanitized_handler():

View File

@@ -1 +1,2 @@
Security/CWE-643/XpathInjection.ql
query: Security/CWE-643/XpathInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -6,11 +6,11 @@ from django.http import HttpResponse
from django.template import Template, Context, Engine, engines
def a(request):
def a(request): # $ Source
value = request.GET['xpath']
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath("/tag[@id='%s']" % value)
r = tree.xpath("/tag[@id='%s']" % value) # $ Alert
urlpatterns = [

View File

@@ -1,5 +1,5 @@
from io import StringIO
from flask import Flask, request
from flask import Flask, request # $ Source
app = Flask(__name__)
@@ -11,7 +11,7 @@ def xpath1():
xpathQuery = request.args.get('xml', '')
f = StringIO('<foo><bar></bar></foo>')
tree = etree.parse(f)
r = tree.xpath(xpathQuery)
r = tree.xpath(xpathQuery) # $ Alert
@app.route("/xpath2")
@@ -20,7 +20,7 @@ def xpath2():
xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.XPath(xpathQuery)
find_text = etree.XPath(xpathQuery) # $ Alert
text = find_text(root)[0]
@@ -29,7 +29,7 @@ def xpath3():
from lxml import etree
xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.XPath(xpathQuery, smart_strings=False)
find_text = etree.XPath(xpathQuery, smart_strings=False) # $ Alert
text = find_text(root)[0]
@@ -38,7 +38,7 @@ def xpath4():
from lxml import etree
xpathQuery = request.args.get('xml', '')
root = etree.XML("<root><a>TEXT</a></root>")
find_text = etree.ETXPath(xpathQuery)
find_text = etree.ETXPath(xpathQuery) # $ Alert
text = find_text(root)[0]
@app.route("/xpath5")
@@ -46,4 +46,4 @@ def xpath5():
import libxml2
xpathQuery = request.args.get('xml', '')
doc = libxml2.parseFile('xpath_injection/credential.xml')
results = doc.xpathEval(xpathQuery)
results = doc.xpathEval(xpathQuery) # $ Alert

View File

@@ -1,3 +1,9 @@
#select
| test.py:8:30:8:33 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:8:30:8:33 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.py:8:19:8:21 | \\s+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:9:32:9:35 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:9:32:9:35 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings starting with '0.9' and with many repetitions of '99'. | test.py:9:25:9:27 | \\d+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:12:17:12:20 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:12:17:12:20 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.py:11:29:11:31 | \\s+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:16:24:16:30 | ControlFlowNode for my_text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:16:24:16:30 | ControlFlowNode for my_text | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.py:18:21:18:23 | \\s+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:21:18:21:21 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:21:18:21:21 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings starting with 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC' and with many repetitions of 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC'. | test.py:20:271:20:272 | .* | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
edges
| test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:2:26:2:32 | ControlFlowNode for request | provenance | |
| test.py:2:26:2:32 | ControlFlowNode for request | test.py:7:12:7:18 | ControlFlowNode for request | provenance | |
@@ -26,9 +32,3 @@ nodes
| test.py:18:28:18:31 | ControlFlowNode for text | semmle.label | ControlFlowNode for text |
| test.py:21:18:21:21 | ControlFlowNode for text | semmle.label | ControlFlowNode for text |
subpaths
#select
| test.py:8:30:8:33 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:8:30:8:33 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.py:8:19:8:21 | \\s+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:9:32:9:35 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:9:32:9:35 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings starting with '0.9' and with many repetitions of '99'. | test.py:9:25:9:27 | \\d+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:12:17:12:20 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:12:17:12:20 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.py:11:29:11:31 | \\s+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:16:24:16:30 | ControlFlowNode for my_text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:16:24:16:30 | ControlFlowNode for my_text | This $@ that depends on a $@ may run slow on strings with many repetitions of ' '. | test.py:18:21:18:23 | \\s+ | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |
| test.py:21:18:21:21 | ControlFlowNode for text | test.py:2:26:2:32 | ControlFlowNode for ImportMember | test.py:21:18:21:21 | ControlFlowNode for text | This $@ that depends on a $@ may run slow on strings starting with 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC' and with many repetitions of 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC'. | test.py:20:271:20:272 | .* | regular expression | test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value |

View File

@@ -1 +1,2 @@
Security/CWE-730/PolynomialReDoS.ql
query: Security/CWE-730/PolynomialReDoS.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,22 +1,22 @@
import re
from flask import Flask, request
from flask import Flask, request # $ Source
app = Flask(__name__)
@app.route("/poly-redos")
def code_execution():
text = request.args.get("text")
re.sub(r"^\s+|\s+$", "", text) # NOT OK
re.match(r"^0\.\d+E?\d+$", text) # NOT OK
re.sub(r"^\s+|\s+$", "", text) # $ Alert # NOT OK
re.match(r"^0\.\d+E?\d+$", text) # $ Alert # NOT OK
reg = re.compile(r"^\s+|\s+$")
reg.sub("", text) # NOT OK
reg.sub("", text) # $ Alert # NOT OK
def indirect(input_reg_str, my_text):
my_reg = re.compile(input_reg_str)
my_reg.sub("", my_text) # NOT OK
my_reg.sub("", my_text) # $ Alert # NOT OK
indirect(r"^\s+|\s+$", text)
reg2 = re.compile(r"(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)(AA|BB)C.*Y")
reg2.sub("", text) # NOT OK
reg2.sub("", text) # $ Alert # NOT OK

View File

@@ -12,7 +12,7 @@ newline = whitespace_optional + newline_only + whitespace_optional
toFlag = re.compile(newline)
# https://github.com/github/codeql-python-CVE-coverage/issues/400
re.compile(r'[+-]?(\d+)*\.\d+%?')
re.compile(r'[+-]?(\d+)*\.\d+%?') # $ Alert
re.compile(r'"""\s+(?:.|\n)*?\s+"""')
re.compile(r'(\{\s+)(\S+)(\s+[^}]+\s+\}\s)')
re.compile(r'".*``.*``.*"')
@@ -27,12 +27,12 @@ re.compile(r'(\.\w+\b)(\s*=\s*)([^;]*)(\s*;)')
simple_email_re = re.compile(r"^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$")
# https://github.com/github/codeql-python-CVE-coverage/issues/249
rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' # $ Alert
'realm=(["\']?)([^"\']*)\\2', re.I)
# https://github.com/github/codeql-python-CVE-coverage/issues/248
gauntlet = re.compile(
r"""^([-/:,#%.'"\s!\w]|\w-\w|'[\s\w]+'\s*|"[\s\w]+"|\([\d,%\.\s]+\))*$""",
r"""^([-/:,#%.'"\s!\w]|\w-\w|'[\s\w]+'\s*|"[\s\w]+"|\([\d,%\.\s]+\))*$""", # $ Alert
flags=re.U
)

View File

@@ -1 +1,2 @@
Security/CWE-730/ReDoS.ql
query: Security/CWE-730/ReDoS.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -3,7 +3,7 @@ import re
# NOT GOOD; attack: "_" + "__".repeat(100)
# Adapted from marked (https://github.com/markedjs/marked), which is licensed
# under the MIT license; see file marked-LICENSE.
bad1 = re.compile(r'''^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)''')
bad1 = re.compile(r'''^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)''') # $ Alert
# GOOD
# Adapted from marked (https://github.com/markedjs/marked), which is licensed
@@ -18,7 +18,7 @@ good2 = re.compile(r'(.*,)+.+')
# NOT GOOD; attack: " '" + "\\\\".repeat(100)
# Adapted from CodeMirror (https://github.com/codemirror/codemirror),
# which is licensed under the MIT license; see file CodeMirror-LICENSE.
bad2 = re.compile(r'''^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?''')
bad2 = re.compile(r'''^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?''') # $ Alert
# GOOD
# Adapted from lulucms2 (https://github.com/yiifans/lulucms2).
@@ -30,53 +30,53 @@ good2 = re.compile(r'''\(\*(?:[\s\S]*?\(\*[\s\S]*?\*\))*[\s\S]*?\*\)''')
good3 = re.compile(r'''^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*''')
# NOT GOOD, variant of good3; attack: "a|\n:|\n" + "||\n".repeat(100)
bad4 = re.compile(r'''^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a''')
bad4 = re.compile(r'''^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a''') # $ Alert
# NOT GOOD; attack: "/" + "\\/a".repeat(100)
# Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog),
# which is licensed under the Apache License 2.0; see file ANodeBlog-LICENSE.
bad5 = re.compile(r'''\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)''')
bad5 = re.compile(r'''\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)''') # $ Alert
# NOT GOOD; attack: "##".repeat(100) + "\na"
# Adapted from CodeMirror (https://github.com/codemirror/codemirror),
# which is licensed under the MIT license; see file CodeMirror-LICENSE.
bad6 = re.compile(r'''^([\s\[\{\(]|#.*)*$''')
bad6 = re.compile(r'''^([\s\[\{\(]|#.*)*$''') # $ Alert
# GOOD
good4 = re.compile(r'''(\r\n|\r|\n)+''')
# BAD - PoC: `node -e "/((?:[^\"\']|\".*?\"|\'.*?\')*?)([(,)]|$)/.test(\"'''''''''''''''''''''''''''''''''''''''''''''\\\"\");"`. It's complicated though, because the regexp still matches something, it just matches the empty-string after the attack string.
actuallyBad = re.compile(r'''((?:[^"']|".*?"|'.*?')*?)([(,)]|$)''')
actuallyBad = re.compile(r'''((?:[^"']|".*?"|'.*?')*?)([(,)]|$)''') # $ Alert
# NOT GOOD; attack: "a" + "[]".repeat(100) + ".b\n"
# Adapted from Knockout (https://github.com/knockout/knockout), which is
# licensed under the MIT license; see file knockout-LICENSE
bad6 = re.compile(r'''^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$''')
bad6 = re.compile(r'''^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$''') # $ Alert
# GOOD
good6 = re.compile(r'''(a|.)*''')
# Testing the NFA - only some of the below are detected.
bad7 = re.compile(r'''^([a-z]+)+$''')
bad8 = re.compile(r'''^([a-z]*)*$''')
bad9 = re.compile(r'''^([a-zA-Z0-9])(([\\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$''')
bad10 = re.compile(r'''^(([a-z])+.)+[A-Z]([a-z])+$''')
bad7 = re.compile(r'''^([a-z]+)+$''') # $ Alert
bad8 = re.compile(r'''^([a-z]*)*$''') # $ Alert
bad9 = re.compile(r'''^([a-zA-Z0-9])(([\\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$''') # $ Alert
bad10 = re.compile(r'''^(([a-z])+.)+[A-Z]([a-z])+$''') # $ Alert
# NOT GOOD; attack: "[" + "][".repeat(100) + "]!"
# Adapted from Prototype.js (https://github.com/prototypejs/prototype), which
# is licensed under the MIT license; see file Prototype.js-LICENSE.
bad11 = re.compile(r'''(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)''')
bad11 = re.compile(r'''(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)''') # $ Alert
# NOT GOOD; attack: "'" + "\\a".repeat(100) + '"'
# Adapted from Prism (https://github.com/PrismJS/prism), which is licensed
# under the MIT license; see file Prism-LICENSE.
bad12 = re.compile(r'''("|')(\\?.)*?\1''')
bad12 = re.compile(r'''("|')(\\?.)*?\1''') # $ Alert
# NOT GOOD
bad13 = re.compile(r'''(b|a?b)*c''')
bad13 = re.compile(r'''(b|a?b)*c''') # $ Alert
# NOT GOOD
bad15 = re.compile(r'''(a|aa?)*b''')
bad15 = re.compile(r'''(a|aa?)*b''') # $ Alert
# GOOD
good7 = re.compile(r'''(.|\n)*!''')
@@ -88,31 +88,31 @@ bad16 = re.compile(r'''(.|\n)*!''')
good8 = re.compile(r'''([\w.]+)*''')
# NOT GOOD
bad17 = re.compile(r'''(a|aa?)*b''')
bad17 = re.compile(r'''(a|aa?)*b''') # $ Alert
# GOOD - not used as regexp
good9 = '(a|aa?)*b'
# NOT GOOD
bad18 = re.compile(r'''(([\s\S]|[^a])*)"''')
bad18 = re.compile(r'''(([\s\S]|[^a])*)"''') # $ Alert
# GOOD - there is no witness in the end that could cause the regexp to not match
good10 = re.compile(r'''([^"']+)*''')
# NOT GOOD
bad20 = re.compile(r'''((.|[^a])*)"''')
bad20 = re.compile(r'''((.|[^a])*)"''') # $ Alert
# GOOD
good10 = re.compile(r'''((a|[^a])*)"''')
# NOT GOOD
bad21 = re.compile(r'''((b|[^a])*)"''')
bad21 = re.compile(r'''((b|[^a])*)"''') # $ Alert
# NOT GOOD
bad22 = re.compile(r'''((G|[^a])*)"''')
bad22 = re.compile(r'''((G|[^a])*)"''') # $ Alert
# NOT GOOD
bad23 = re.compile(r'''(([0-9]|[^a])*)"''')
bad23 = re.compile(r'''(([0-9]|[^a])*)"''') # $ Alert
# NOT GOOD
bad24 = re.compile(r'''(?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))?''')
@@ -124,55 +124,55 @@ bad25 = re.compile(r'''"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"''')
bad26 = re.compile(r'''"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"''')
# NOT GOOD
bad27 = re.compile(r'''(([a-z]|[d-h])*)"''')
bad27 = re.compile(r'''(([a-z]|[d-h])*)"''') # $ Alert
# NOT GOOD
bad27 = re.compile(r'''(([^a-z]|[^0-9])*)"''')
bad27 = re.compile(r'''(([^a-z]|[^0-9])*)"''') # $ Alert
# NOT GOOD
bad28 = re.compile(r'''((\d|[0-9])*)"''')
bad28 = re.compile(r'''((\d|[0-9])*)"''') # $ Alert
# NOT GOOD
bad29 = re.compile(r'''((\s|\s)*)"''')
bad29 = re.compile(r'''((\s|\s)*)"''') # $ Alert
# NOT GOOD
bad30 = re.compile(r'''((\w|G)*)"''')
bad30 = re.compile(r'''((\w|G)*)"''') # $ Alert
# GOOD
good11 = re.compile(r'''((\s|\d)*)"''')
# NOT GOOD
bad31 = re.compile(r'''((\d|\w)*)"''')
bad31 = re.compile(r'''((\d|\w)*)"''') # $ Alert
# NOT GOOD
bad32 = re.compile(r'''((\d|5)*)"''')
bad32 = re.compile(r'''((\d|5)*)"''') # $ Alert
# NOT GOOD
bad33 = re.compile(r'''((\s|[\f])*)"''')
bad33 = re.compile(r'''((\s|[\f])*)"''') # $ Alert
# NOT GOOD
bad34 = re.compile(r'''((\s|[\v]|\\v)*)"''')
bad34 = re.compile(r'''((\s|[\v]|\\v)*)"''') # $ Alert
# NOT GOOD
bad35 = re.compile(r'''((\f|[\f])*)"''')
bad35 = re.compile(r'''((\f|[\f])*)"''') # $ Alert
# NOT GOOD
bad36 = re.compile(r'''((\W|\D)*)"''')
bad36 = re.compile(r'''((\W|\D)*)"''') # $ Alert
# NOT GOOD
bad37 = re.compile(r'''((\S|\w)*)"''')
bad37 = re.compile(r'''((\S|\w)*)"''') # $ Alert
# NOT GOOD
bad38 = re.compile(r'''((\S|[\w])*)"''')
bad38 = re.compile(r'''((\S|[\w])*)"''') # $ Alert
# NOT GOOD
bad39 = re.compile(r'''((1s|[\da-z])*)"''')
bad39 = re.compile(r'''((1s|[\da-z])*)"''') # $ Alert
# NOT GOOD
bad40 = re.compile(r'''((0|[\d])*)"''')
bad40 = re.compile(r'''((0|[\d])*)"''') # $ Alert
# NOT GOOD
bad41 = re.compile(r'''(([\d]+)*)"''')
bad41 = re.compile(r'''(([\d]+)*)"''') # $ Alert
# GOOD - there is no witness in the end that could cause the regexp to not match
good12 = re.compile(r'''(\d+(X\d+)?)+''')
@@ -184,49 +184,49 @@ good13 = re.compile(r'''([0-9]+(X[0-9]*)?)*''')
good15 = re.compile(r'''^([^>]+)*(>|$)''')
# NOT GOOD
bad43 = re.compile(r'''^([^>a]+)*(>|$)''')
bad43 = re.compile(r'''^([^>a]+)*(>|$)''') # $ Alert
# NOT GOOD
bad44 = re.compile(r'''(\n\s*)+$''')
bad44 = re.compile(r'''(\n\s*)+$''') # $ Alert
# NOT GOOD
bad45 = re.compile(r'''^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})''')
bad45 = re.compile(r'''^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})''') # $ Alert
# NOT GOOD
bad46 = re.compile(r'''\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}''')
bad46 = re.compile(r'''\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}''') # $ Alert
# NOT GOOD
bad47 = re.compile(r'''(a+|b+|c+)*c''')
bad47 = re.compile(r'''(a+|b+|c+)*c''') # $ Alert
# NOT GOOD
bad48 = re.compile(r'''(((a+a?)*)+b+)''')
bad48 = re.compile(r'''(((a+a?)*)+b+)''') # $ Alert
# NOT GOOD
bad49 = re.compile(r'''(a+)+bbbb''')
bad49 = re.compile(r'''(a+)+bbbb''') # $ Alert
# GOOD
good16 = re.compile(r'''(a+)+aaaaa*a+''')
# NOT GOOD
bad50 = re.compile(r'''(a+)+aaaaa$''')
bad50 = re.compile(r'''(a+)+aaaaa$''') # $ Alert
# GOOD
good17 = re.compile(r'''(\n+)+\n\n''')
# NOT GOOD
bad51 = re.compile(r'''(\n+)+\n\n$''')
bad51 = re.compile(r'''(\n+)+\n\n$''') # $ Alert
# NOT GOOD
bad52 = re.compile(r'''([^X]+)*$''')
bad52 = re.compile(r'''([^X]+)*$''') # $ Alert
# NOT GOOD
bad53 = re.compile(r'''(([^X]b)+)*$''')
bad53 = re.compile(r'''(([^X]b)+)*$''') # $ Alert
# GOOD
good18 = re.compile(r'''(([^X]b)+)*($|[^X]b)''')
# NOT GOOD
bad54 = re.compile(r'''(([^X]b)+)*($|[^X]c)''')
bad54 = re.compile(r'''(([^X]b)+)*($|[^X]c)''') # $ Alert
# GOOD
good20 = re.compile(r'''((ab)+)*ababab''')
@@ -238,13 +238,13 @@ good21 = re.compile(r'''((ab)+)*abab(ab)*(ab)+''')
good22 = re.compile(r'''((ab)+)*''')
# NOT GOOD
bad55 = re.compile(r'''((ab)+)*$''')
bad55 = re.compile(r'''((ab)+)*$''') # $ Alert
# GOOD
good23 = re.compile(r'''((ab)+)*[a1][b1][a2][b2][a3][b3]''')
# NOT GOOD
bad56 = re.compile(r'''([\n\s]+)*(.)''')
bad56 = re.compile(r'''([\n\s]+)*(.)''') # $ Alert
# GOOD - any witness passes through the accept state.
good24 = re.compile(r'''(A*A*X)*''')
@@ -253,76 +253,76 @@ good24 = re.compile(r'''(A*A*X)*''')
good26 = re.compile(r'''([^\\\]]+)*''')
# NOT GOOD
bad59 = re.compile(r'''(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-''')
bad59 = re.compile(r'''(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-''') # $ Alert
# NOT GOOD
bad60 = re.compile(r'''(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-''')
bad60 = re.compile(r'''(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-''') # $ Alert
# NOT GOOD
bad61 = re.compile(r'''(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-''')
bad61 = re.compile(r'''(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-''') # $ Alert
# GOOD
good27 = re.compile(r'''(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelatedstringcomparedtotheotherstring)*-''')
# GOOD (but false positive caused by the extractor converting all four unpaired surrogates to \uFFFD)
good28 = re.compile('''foo([\uDC66\uDC67]|[\uDC68\uDC69])*foo''')
good28 = re.compile('''foo([\uDC66\uDC67]|[\uDC68\uDC69])*foo''') # $ Alert
# GOOD (but false positive caused by the extractor converting all four unpaired surrogates to \uFFFD)
good29 = re.compile('''foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo''')
good29 = re.compile('''foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo''') # $ Alert
# NOT GOOD (but cannot currently construct a prefix)
bad62 = re.compile(r'''a{2,3}(b+)+X''')
bad62 = re.compile(r'''a{2,3}(b+)+X''') # $ Alert
# NOT GOOD (and a good prefix test)
bad63 = re.compile(r'''^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>''')
bad63 = re.compile(r'''^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>''') # $ Alert
# GOOD
good30 = re.compile(r'''(a+)*[\s\S][\s\S][\s\S]?''')
# GOOD - but we fail to see that repeating the attack string ends in the "accept any" state (due to not parsing the range `[\s\S]{2,3}`).
good31 = re.compile(r'''(a+)*[\s\S]{2,3}''')
good31 = re.compile(r'''(a+)*[\s\S]{2,3}''') # $ Alert
# GOOD - but we spuriously conclude that a rejecting suffix exists (due to not parsing the range `[\s\S]{2,}` when constructing the NFA).
good32 = re.compile(r'''(a+)*([\s\S]{2,}|X)$''')
good32 = re.compile(r'''(a+)*([\s\S]{2,}|X)$''') # $ Alert
# GOOD
good33 = re.compile(r'''(a+)*([\s\S]*|X)$''')
# NOT GOOD
bad64 = re.compile(r'''((a+)*$|[\s\S]+)''')
bad64 = re.compile(r'''((a+)*$|[\s\S]+)''') # $ Alert
# GOOD - but still flagged. The only change compared to the above is the order of alternatives, which we don't model.
good34 = re.compile(r'''([\s\S]+|(a+)*$)''')
good34 = re.compile(r'''([\s\S]+|(a+)*$)''') # $ Alert
# GOOD
good35 = re.compile(r'''((;|^)a+)+$''')
# NOT GOOD (a good prefix test)
bad65 = re.compile(r'''(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f''')
bad65 = re.compile(r'''(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f''') # $ Alert
# NOT GOOD
bad66 = re.compile(r'''^ab(c+)+$''')
bad66 = re.compile(r'''^ab(c+)+$''') # $ Alert
# NOT GOOD
bad67 = re.compile(r'''(\d(\s+)*){20}''')
bad67 = re.compile(r'''(\d(\s+)*){20}''') # $ Alert
# GOOD - but we spuriously conclude that a rejecting suffix exists.
good36 = re.compile(r'''(([^/]|X)+)(\/[\s\S]*)*$''')
good36 = re.compile(r'''(([^/]|X)+)(\/[\s\S]*)*$''') # $ Alert
# GOOD - but we spuriously conclude that a rejecting suffix exists.
good37 = re.compile(r'''^((x([^Y]+)?)*(Y|$))''')
good37 = re.compile(r'''^((x([^Y]+)?)*(Y|$))''') # $ Alert
# NOT GOOD
bad68 = re.compile(r'''(a*)+b''')
bad68 = re.compile(r'''(a*)+b''') # $ Alert
# NOT GOOD
bad69 = re.compile(r'''foo([\w-]*)+bar''')
bad69 = re.compile(r'''foo([\w-]*)+bar''') # $ Alert
# NOT GOOD
bad70 = re.compile(r'''((ab)*)+c''')
bad70 = re.compile(r'''((ab)*)+c''') # $ Alert
# NOT GOOD
bad71 = re.compile(r'''(a?a?)*b''')
bad71 = re.compile(r'''(a?a?)*b''') # $ Alert
# GOOD
good38 = re.compile(r'''(a?)*b''')
@@ -331,44 +331,44 @@ good38 = re.compile(r'''(a?)*b''')
bad72 = re.compile(r'''(c?a?)*b''')
# NOT GOOD
bad73 = re.compile(r'''(?:a|a?)+b''')
bad73 = re.compile(r'''(?:a|a?)+b''') # $ Alert
# NOT GOOD - but not detected.
bad74 = re.compile(r'''(a?b?)*$''')
# NOT GOOD
bad76 = re.compile(r'''PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)''')
bad76 = re.compile(r'''PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)''') # $ Alert
# NOT GOOD - but not detected
bad77 = re.compile(r'''^((a)+\w)+$''')
bad77 = re.compile(r'''^((a)+\w)+$''') # $ Alert
# NOT GOOD
bad78 = re.compile(r'''^(b+.)+$''')
bad78 = re.compile(r'''^(b+.)+$''') # $ Alert
# GOOD
good39 = re.compile(r'''a*b''')
# All 4 bad combinations of nested * and +
bad79 = re.compile(r'''(a*)*b''')
bad80 = re.compile(r'''(a+)*b''')
bad81 = re.compile(r'''(a*)+b''')
bad82 = re.compile(r'''(a+)+b''')
bad79 = re.compile(r'''(a*)*b''') # $ Alert
bad80 = re.compile(r'''(a+)*b''') # $ Alert
bad81 = re.compile(r'''(a*)+b''') # $ Alert
bad82 = re.compile(r'''(a+)+b''') # $ Alert
# GOOD
good40 = re.compile(r'''(a|b)+''')
good41 = re.compile(r'''(?:[\s;,"'<>(){}|[\]@=+*]|:(?![/\\]))+''') # parses wrongly, sees column 42 as a char set start
# NOT GOOD
bad83 = re.compile(r'''^((?:a{|-)|\w\{)+X$''')
bad84 = re.compile(r'''^((?:a{0|-)|\w\{\d)+X$''')
bad85 = re.compile(r'''^((?:a{0,|-)|\w\{\d,)+X$''')
bad86 = re.compile(r'''^((?:a{0,2|-)|\w\{\d,\d)+X$''')
bad83 = re.compile(r'''^((?:a{|-)|\w\{)+X$''') # $ Alert
bad84 = re.compile(r'''^((?:a{0|-)|\w\{\d)+X$''') # $ Alert
bad85 = re.compile(r'''^((?:a{0,|-)|\w\{\d,)+X$''') # $ Alert
bad86 = re.compile(r'''^((?:a{0,2|-)|\w\{\d,\d)+X$''') # $ Alert
# GOOD:
good42 = re.compile(r'''^((?:a{0,2}|-)|\w\{\d,\d\})+X$''')
# NOT GOOD
bad87 = re.compile(r'X(\u0061|a)*Y')
bad87 = re.compile(r'X(\u0061|a)*Y') # $ Alert
# GOOD
good43 = re.compile(r'X(\u0061|b)+Y')
@@ -377,17 +377,17 @@ good43 = re.compile(r'X(\u0061|b)+Y')
good44 = re.compile(r'("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)')
# BAD
bad88 = re.compile(r'/("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)X')
bad89 = re.compile(r'/("[^"]*?"|[^"\s]+)+(?=X)')
bad88 = re.compile(r'/("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)X') # $ Alert
bad89 = re.compile(r'/("[^"]*?"|[^"\s]+)+(?=X)') # $ Alert
# BAD
bad90 = re.compile(r'\A(\d|0)*x')
bad91 = re.compile(r'(\d|0)*\Z')
bad92 = re.compile(r'\b(\d|0)*x')
bad90 = re.compile(r'\A(\d|0)*x') # $ Alert
bad91 = re.compile(r'(\d|0)*\Z') # $ Alert
bad92 = re.compile(r'\b(\d|0)*x') # $ Alert
# GOOD
stress1 = re.compile(r"(?<!&)#(\w+|(?:[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u2388\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2694\u2696\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD79\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED0\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3]|\uD83E[\uDD10-\uDD18\uDD80-\uDD84\uDDC0]|(?:0\u20E3|1\u20E3|2\u20E3|3\u20E3|4\u20E3|5\u20E3|6\u20E3|7\u20E3|8\u20E3|9\u20E3|#\u20E3|\\*\u20E3|\uD83C(?:\uDDE6\uD83C(?:\uDDEB|\uDDFD|\uDDF1|\uDDF8|\uDDE9|\uDDF4|\uDDEE|\uDDF6|\uDDEC|\uDDF7|\uDDF2|\uDDFC|\uDDE8|\uDDFA|\uDDF9|\uDDFF|\uDDEA)|\uDDE7\uD83C(?:\uDDF8|\uDDED|\uDDE9|\uDDE7|\uDDFE|\uDDEA|\uDDFF|\uDDEF|\uDDF2|\uDDF9|\uDDF4|\uDDE6|\uDDFC|\uDDFB|\uDDF7|\uDDF3|\uDDEC|\uDDEB|\uDDEE|\uDDF6|\uDDF1)|\uDDE8\uD83C(?:\uDDF2|\uDDE6|\uDDFB|\uDDEB|\uDDF1|\uDDF3|\uDDFD|\uDDF5|\uDDE8|\uDDF4|\uDDEC|\uDDE9|\uDDF0|\uDDF7|\uDDEE|\uDDFA|\uDDFC|\uDDFE|\uDDFF|\uDDED)|\uDDE9\uD83C(?:\uDDFF|\uDDF0|\uDDEC|\uDDEF|\uDDF2|\uDDF4|\uDDEA)|\uDDEA\uD83C(?:\uDDE6|\uDDE8|\uDDEC|\uDDF7|\uDDEA|\uDDF9|\uDDFA|\uDDF8|\uDDED)|\uDDEB\uD83C(?:\uDDF0|\uDDF4|\uDDEF|\uDDEE|\uDDF7|\uDDF2)|\uDDEC\uD83C(?:\uDDF6|\uDDEB|\uDDE6|\uDDF2|\uDDEA|\uDDED|\uDDEE|\uDDF7|\uDDF1|\uDDE9|\uDDF5|\uDDFA|\uDDF9|\uDDEC|\uDDF3|\uDDFC|\uDDFE|\uDDF8|\uDDE7)|\uDDED\uD83C(?:\uDDF7|\uDDF9|\uDDF2|\uDDF3|\uDDF0|\uDDFA)|\uDDEE\uD83C(?:\uDDF4|\uDDE8|\uDDF8|\uDDF3|\uDDE9|\uDDF7|\uDDF6|\uDDEA|\uDDF2|\uDDF1|\uDDF9)|\uDDEF\uD83C(?:\uDDF2|\uDDF5|\uDDEA|\uDDF4)|\uDDF0\uD83C(?:\uDDED|\uDDFE|\uDDF2|\uDDFF|\uDDEA|\uDDEE|\uDDFC|\uDDEC|\uDDF5|\uDDF7|\uDDF3)|\uDDF1\uD83C(?:\uDDE6|\uDDFB|\uDDE7|\uDDF8|\uDDF7|\uDDFE|\uDDEE|\uDDF9|\uDDFA|\uDDF0|\uDDE8)|\uDDF2\uD83C(?:\uDDF4|\uDDF0|\uDDEC|\uDDFC|\uDDFE|\uDDFB|\uDDF1|\uDDF9|\uDDED|\uDDF6|\uDDF7|\uDDFA|\uDDFD|\uDDE9|\uDDE8|\uDDF3|\uDDEA|\uDDF8|\uDDE6|\uDDFF|\uDDF2|\uDDF5|\uDDEB)|\uDDF3\uD83C(?:\uDDE6|\uDDF7|\uDDF5|\uDDF1|\uDDE8|\uDDFF|\uDDEE|\uDDEA|\uDDEC|\uDDFA|\uDDEB|\uDDF4)|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C(?:\uDDEB|\uDDF0|\uDDFC|\uDDF8|\uDDE6|\uDDEC|\uDDFE|\uDDEA|\uDDED|\uDDF3|\uDDF1|\uDDF9|\uDDF7|\uDDF2)|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C(?:\uDDEA|\uDDF4|\uDDFA|\uDDFC|\uDDF8)|\uDDF8\uD83C(?:\uDDFB|\uDDF2|\uDDF9|\uDDE6|\uDDF3|\uDDE8|\uDDF1|\uDDEC|\uDDFD|\uDDF0|\uDDEE|\uDDE7|\uDDF4|\uDDF8|\uDDED|\uDDE9|\uDDF7|\uDDEF|\uDDFF|\uDDEA|\uDDFE)|\uDDF9\uD83C(?:\uDDE9|\uDDEB|\uDDFC|\uDDEF|\uDDFF|\uDDED|\uDDF1|\uDDEC|\uDDF0|\uDDF4|\uDDF9|\uDDE6|\uDDF3|\uDDF7|\uDDF2|\uDDE8|\uDDFB)|\uDDFA\uD83C(?:\uDDEC|\uDDE6|\uDDF8|\uDDFE|\uDDF2|\uDDFF)|\uDDFB\uD83C(?:\uDDEC|\uDDE8|\uDDEE|\uDDFA|\uDDE6|\uDDEA|\uDDF3)|\uDDFC\uD83C(?:\uDDF8|\uDDEB)|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C(?:\uDDF9|\uDDEA)|\uDDFF\uD83C(?:\uDDE6|\uDDF2|\uDDFC))))[\ufe00-\ufe0f\u200d]?)+")
MY_REG = r'X(\u0061|a)*Y'
MY_REG = r'X(\u0061|a)*Y' # $ Alert
def matcher(name):
re.match(MY_REG, name)

View File

@@ -2,12 +2,12 @@ import re
# Treatment of escapes
re.compile(r"X([^\.]|\.)*$") # No ReDoS.
re.compile(r"X(Æ|\Æ)+$") # Has ReDoS.
re.compile(r"X(Æ|\Æ)+$") # $ Alert # Has ReDoS.
# Treatment of line breaks
re.compile(r'(?:.|\n)*b') # No ReDoS.
re.compile(r'(?:.|\n)*b', re.DOTALL) # Has ReDoS.
re.compile(r'(?:.|\n)*b', re.DOTALL) # $ Alert # Has ReDoS.
re.compile(r'(?i)(?:.|\n)*b') # No ReDoS.
re.compile(r'(?s)(?:.|\n)*b') # Has ReDoS.
re.compile(r'(?is)(?:.|\n)*b') # Has ReDoS.
re.compile(r'(?is)X(?:.|\n)*Y') # Has ReDoS.
re.compile(r'(?s)(?:.|\n)*b') # $ Alert # Has ReDoS.
re.compile(r'(?is)(?:.|\n)*b') # $ Alert # Has ReDoS.
re.compile(r'(?is)X(?:.|\n)*Y') # $ Alert # Has ReDoS.

View File

@@ -1 +1,2 @@
Security/CWE-730/RegexInjection.ql
query: Security/CWE-730/RegexInjection.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,4 +1,4 @@
from flask import request, Flask
from flask import request, Flask # $ Source
import re
app = Flask(__name__)
@@ -11,7 +11,7 @@ def direct():
"""
unsafe_pattern = request.args["pattern"]
re.search(unsafe_pattern, "")
re.search(unsafe_pattern, "") # $ Alert
@app.route("/compile")
@@ -22,7 +22,7 @@ def compile():
"""
unsafe_pattern = request.args["pattern"]
compiled_pattern = re.compile(unsafe_pattern)
compiled_pattern = re.compile(unsafe_pattern) # $ Alert
compiled_pattern.search("")
@@ -34,7 +34,7 @@ def compile_direct():
"""
unsafe_pattern = request.args["pattern"]
re.compile(unsafe_pattern).search("")
re.compile(unsafe_pattern).search("") # $ Alert
# if __name__ == "__main__":
# app.run(debug=True)

View File

@@ -1 +1,2 @@
Security/CWE-732/WeakFilePermissions.ql
query: Security/CWE-732/WeakFilePermissions.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

Some files were not shown because too many files have changed in this diff Show More