mirror of
https://github.com/github/codeql.git
synced 2026-04-26 09:15:12 +02:00
python: Inline expectation should have space after $
This was a regex-find-replace from `# \$(?! )` (using a negative lookahead) to `# $ `.
This commit is contained in:
@@ -61,35 +61,35 @@ try:
|
||||
val.attr
|
||||
except Exception:
|
||||
print (2)
|
||||
except AttributeError: # $Alert[py/unreachable-except]
|
||||
except AttributeError: # $ Alert[py/unreachable-except]
|
||||
print (3)
|
||||
|
||||
class MyExc(ValueError):
|
||||
pass
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
except MyExc: # $MISSING:Alert[py/unreachable-except] # Missing due to dataflow limitiation preventing MyExc from being tracked here.
|
||||
pass
|
||||
pass
|
||||
except MyExc: # $ MISSING:Alert[py/unreachable-except] # Missing due to dataflow limitiation preventing MyExc from being tracked here.
|
||||
pass
|
||||
|
||||
class MyBaseExc(Exception):
|
||||
pass
|
||||
pass
|
||||
|
||||
class MySubExc(MyBaseExc):
|
||||
pass
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except MyBaseExc:
|
||||
pass
|
||||
except MySubExc: # $MISSING:Alert[py/unreachable-except] # Missing due to dataflow limitation preventing MyExc from being tracked here.
|
||||
pass
|
||||
pass
|
||||
except MySubExc: # $ MISSING:Alert[py/unreachable-except] # Missing due to dataflow limitation preventing MyExc from being tracked here.
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
#Catch BaseException
|
||||
def catch_base_exception():
|
||||
try:
|
||||
@@ -97,13 +97,13 @@ def catch_base_exception():
|
||||
except BaseException:
|
||||
#Consumes KeyboardInterrupt
|
||||
pass
|
||||
|
||||
|
||||
def catch_base_exception_ok():
|
||||
try:
|
||||
illegal_raise()
|
||||
except BaseException:
|
||||
raise
|
||||
|
||||
|
||||
def legal_handler1():
|
||||
try:
|
||||
illegal_raise()
|
||||
|
||||
@@ -14,17 +14,17 @@ class Normal(object):
|
||||
|
||||
# not ok
|
||||
@classmethod
|
||||
def n_cmethod(self): # $shouldBeCls
|
||||
def n_cmethod(self): # $ shouldBeCls
|
||||
pass
|
||||
|
||||
# not ok
|
||||
@classmethod
|
||||
def n_cmethod2(): # $shouldBeCls
|
||||
def n_cmethod2(): # $ shouldBeCls
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@id
|
||||
def n_dec(any_name): # $shouldBeCls
|
||||
def n_dec(any_name): # $ shouldBeCls
|
||||
pass
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@ class Class(type):
|
||||
def __init__(cls):
|
||||
pass
|
||||
|
||||
def c_method(y): # $shouldBeCls
|
||||
def c_method(y): # $ shouldBeCls
|
||||
pass
|
||||
|
||||
def c_ok(cls):
|
||||
pass
|
||||
|
||||
# technically we could alert on mixing self for metaclasses with cls for metaclasses in the same codebase,
|
||||
# technically we could alert on mixing self for metaclasses with cls for metaclasses in the same codebase,
|
||||
# but it's probably not too significant.
|
||||
def c_self_ok(self):
|
||||
pass
|
||||
@@ -48,13 +48,13 @@ class Class(type):
|
||||
|
||||
class NonSelf(object):
|
||||
|
||||
def __init__(x): # $shouldBeSelf
|
||||
def __init__(x): # $ shouldBeSelf
|
||||
pass
|
||||
|
||||
def s_method(y): # $shouldBeSelf
|
||||
def s_method(y): # $ shouldBeSelf
|
||||
pass
|
||||
|
||||
def s_method2(): # $shouldBeSelf
|
||||
def s_method2(): # $ shouldBeSelf
|
||||
pass
|
||||
|
||||
def s_ok(self):
|
||||
@@ -68,7 +68,7 @@ class NonSelf(object):
|
||||
def s_cmethod(cls):
|
||||
pass
|
||||
|
||||
# we allow methods that are used in class initialization, but only detect this case when they are called.
|
||||
# we allow methods that are used in class initialization, but only detect this case when they are called.
|
||||
def s_smethod2(ok): # $ SPURIOUS: shouldBeSelf
|
||||
pass
|
||||
s_smethod2 = staticmethod(s_smethod2)
|
||||
@@ -123,7 +123,7 @@ Z().meth(0)
|
||||
def weird_decorator(f):
|
||||
def g(self):
|
||||
return f()
|
||||
return g
|
||||
return g
|
||||
|
||||
class B:
|
||||
@weird_decorator
|
||||
@@ -152,7 +152,7 @@ class SpecialMethodNames(object):
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
@dataclass
|
||||
@dataclass
|
||||
class A:
|
||||
# Lambdas used in initilisation aren't methods.
|
||||
x: int = field(default_factory = lambda: 2)
|
||||
x: int = field(default_factory = lambda: 2)
|
||||
|
||||
@@ -21,13 +21,13 @@ class Base(object):
|
||||
|
||||
class Derived(Base):
|
||||
|
||||
def meth1(self, spam): # $Alert[py/inheritance/signature-mismatch] # Has 1 more arg, base called in Base.foo
|
||||
def meth1(self, spam): # $ Alert[py/inheritance/signature-mismatch] # Has 1 more arg, base called in Base.foo
|
||||
pass
|
||||
|
||||
def meth2(self): # $Alert[py/inheritance/signature-mismatch] # Has 1 fewer arg, base called in Base.foo
|
||||
def meth2(self): # $ Alert[py/inheritance/signature-mismatch] # Has 1 fewer arg, base called in Base.foo
|
||||
pass
|
||||
|
||||
def meth3(self, eggs): # $Alert[py/inheritance/signature-mismatch] # Has 1 more arg. Method is not called.
|
||||
def meth3(self, eggs): # $ Alert[py/inheritance/signature-mismatch] # Has 1 more arg. Method is not called.
|
||||
pass
|
||||
|
||||
def bar(self):
|
||||
@@ -66,12 +66,12 @@ class BlameBase(object):
|
||||
|
||||
class Correct1(BlameBase):
|
||||
|
||||
def meth(self, arg): # $Alert[py/inheritance/signature-mismatch] # Has 1 more arg. The incorrect-overridden-method query would alert for the base method in this case.
|
||||
def meth(self, arg): # $ Alert[py/inheritance/signature-mismatch] # Has 1 more arg. The incorrect-overridden-method query would alert for the base method in this case.
|
||||
pass
|
||||
|
||||
class Correct2(BlameBase):
|
||||
|
||||
def meth(self, arg): # $Alert[py/inheritance/signature-mismatch] # Has 1 more arg
|
||||
def meth(self, arg): # $ Alert[py/inheritance/signature-mismatch] # Has 1 more arg
|
||||
pass
|
||||
|
||||
c = Correct2()
|
||||
@@ -122,28 +122,28 @@ class Base2:
|
||||
|
||||
class Derrived2(Base2):
|
||||
|
||||
def meth1(self): pass # $Alert[py/inheritance/signature-mismatch] # Weak mismatch (base may be called with 2 args. only alert if mismatching call exists)
|
||||
def meth1(self): pass # $ Alert[py/inheritance/signature-mismatch] # Weak mismatch (base may be called with 2 args. only alert if mismatching call exists)
|
||||
|
||||
def meth2(self): pass # No alert (weak mismatch, but not called)
|
||||
|
||||
def meth3(self, x=1): pass # No alert (no mismatch - all base calls are valid for sub)
|
||||
|
||||
def meth4(self, x, y, z=1): pass # $Alert[py/inheritance/signature-mismatch] # sub min > base max (strong mismatch)
|
||||
def meth4(self, x, y, z=1): pass # $ Alert[py/inheritance/signature-mismatch] # sub min > base max (strong mismatch)
|
||||
|
||||
def meth5(self, x, y=1): pass # $Alert[py/inheritance/signature-mismatch]
|
||||
def meth5(self, x, y=1): pass # $ Alert[py/inheritance/signature-mismatch]
|
||||
|
||||
def meth6(self, x): pass # $Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with 3+ args)
|
||||
def meth6(self, x): pass # $ Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with 3+ args)
|
||||
|
||||
def meth7(self, x, *ys): pass # $Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with 1 arg only)
|
||||
def meth7(self, x, *ys): pass # $ Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with 1 arg only)
|
||||
|
||||
def meth8(self, x, z): pass # $Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with arg named y)
|
||||
def meth8(self, x, z): pass # $ Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with arg named y)
|
||||
|
||||
def meth9(self, x, z): pass # No alert (never called with wrong keyword arg)
|
||||
|
||||
def meth10(self, x, **kwargs): pass # No alert (y is kw-only arg in base, calls that use it are valid for sub)
|
||||
|
||||
def meth11(self, x, z, **kwargs): pass # $MISSING:Alert[py/inheritance/signature-mismatch] # call using y kw-arg is invalid due to not specifying z, but this is not detected. Likely a fairly niche situation.
|
||||
def meth11(self, x, z, **kwargs): pass # $ MISSING:Alert[py/inheritance/signature-mismatch] # call using y kw-arg is invalid due to not specifying z, but this is not detected. Likely a fairly niche situation.
|
||||
|
||||
def meth12(self): pass # $Alert[py/inheritance/signature-mismatch] # call including extra kwarg invalid
|
||||
def meth12(self): pass # $ Alert[py/inheritance/signature-mismatch] # call including extra kwarg invalid
|
||||
|
||||
def meth13(self, /, y): pass # $Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with arg named x), however meth13 is incorrectly detected as having 2 minimum positional arguments, whereas x is kw-only; resulting in the witness call not being detected as a valid call to Base2.meth13.
|
||||
def meth13(self, /, y): pass # $ Alert[py/inheritance/signature-mismatch] # weak mismatch (base may be called with arg named x), however meth13 is incorrectly detected as having 2 minimum positional arguments, whereas x is kw-only; resulting in the witness call not being detected as a valid call to Base2.meth13.
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
def not_close1():
|
||||
f1 = open("filename") # $ Alert # not closed on exception
|
||||
f1.write("Error could occur")
|
||||
f1.close()
|
||||
f1.close()
|
||||
|
||||
def not_close2():
|
||||
f2 = open("filename") # $ Alert
|
||||
f2 = open("filename") # $ Alert
|
||||
|
||||
def closed3():
|
||||
f3 = open("filename")
|
||||
@@ -46,7 +46,7 @@ def closed7():
|
||||
def not_closed8():
|
||||
f8 = None
|
||||
try:
|
||||
f8 = open("filename") # $ MISSING:Alert # not closed on exception
|
||||
f8 = open("filename") # $ MISSING:Alert # not closed on exception
|
||||
f8.write("Error could occur")
|
||||
finally:
|
||||
if f8 is None: # We don't precisely consider this condition, so this result is MISSING. However, this seems uncommon.
|
||||
@@ -88,7 +88,7 @@ def doesnt_raise(*args):
|
||||
pass
|
||||
|
||||
def mostly_closed12():
|
||||
f12 = open("filename")
|
||||
f12 = open("filename")
|
||||
try:
|
||||
f12.write("IOError could occur")
|
||||
f12.write("IOError could occur")
|
||||
@@ -105,7 +105,7 @@ def opener_func2(name):
|
||||
return t1
|
||||
|
||||
def not_closed13(name):
|
||||
f13 = open(name) # $ Alert
|
||||
f13 = open(name) # $ Alert
|
||||
f13.write("Hello")
|
||||
|
||||
def may_not_be_closed14(name):
|
||||
@@ -120,7 +120,7 @@ def closer2(t3):
|
||||
closer1(t3)
|
||||
|
||||
def closed15():
|
||||
f15 = opener_func2() # $ SPURIOUS:Alert
|
||||
f15 = opener_func2() # $ SPURIOUS:Alert
|
||||
closer2(f15) # We don't detect that this call closes the file, so this result is SPURIOUS.
|
||||
|
||||
|
||||
@@ -151,11 +151,11 @@ def not_closed17():
|
||||
#With statement will close the fp
|
||||
def closed18(path):
|
||||
try:
|
||||
f18 = open(path)
|
||||
f18 = open(path)
|
||||
except IOError as ex:
|
||||
print(ex)
|
||||
raise ex
|
||||
with f18:
|
||||
with f18:
|
||||
f18.read()
|
||||
|
||||
class Closed19(object):
|
||||
@@ -245,7 +245,7 @@ def not_closed22(path):
|
||||
f22.close()
|
||||
|
||||
def not_closed23(path):
|
||||
f23 = open(path, "w") # $ Alert
|
||||
f23 = open(path, "w") # $ Alert
|
||||
wr = FileWrapper(f23)
|
||||
|
||||
def closed24(path):
|
||||
@@ -253,11 +253,11 @@ def closed24(path):
|
||||
try:
|
||||
f24.write("hi")
|
||||
except:
|
||||
pass
|
||||
pass
|
||||
f24.close()
|
||||
|
||||
def closed25(path):
|
||||
from django.http import FileResponse
|
||||
from django.http import FileResponse
|
||||
return FileResponse(open(path))
|
||||
|
||||
import os
|
||||
@@ -266,13 +266,13 @@ def closed26(path):
|
||||
os.close(fd)
|
||||
|
||||
def not_closed27(path):
|
||||
fd = os.open(path, "w") # $Alert # not closed on exception
|
||||
fd = os.open(path, "w") # $ Alert # not closed on exception
|
||||
f27 = os.fdopen(fd, "w")
|
||||
f27.write("hi")
|
||||
f27.close()
|
||||
|
||||
def closed28(path):
|
||||
fd = os.open(path, os.O_WRONLY)
|
||||
fd = os.open(path, os.O_WRONLY)
|
||||
f28 = os.fdopen(fd, "w")
|
||||
try:
|
||||
f28.write("hi")
|
||||
@@ -282,9 +282,9 @@ def closed28(path):
|
||||
def closed29(path):
|
||||
# Due to an approximation in CFG reachability for performance, it is not detected that the `write` call that may raise occurs after the file has already been closed.
|
||||
# We presume this case to be uncommon.
|
||||
f28 = open(path) # $SPURIOUS:Alert # not closed on exception
|
||||
f28 = open(path) # $ SPURIOUS:Alert # not closed on exception
|
||||
f28.close()
|
||||
f28.write("already closed")
|
||||
f28.write("already closed")
|
||||
|
||||
# False positive in a previous implementation:
|
||||
|
||||
@@ -294,11 +294,11 @@ class NotWrapper:
|
||||
fp.close()
|
||||
|
||||
def do_something(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
def closed30(path):
|
||||
# Combination of approximations resulted in this FP:
|
||||
# - NotWrapper is treated as a wrapper class as a file handle is passed to it
|
||||
# - NotWrapper is treated as a wrapper class as a file handle is passed to it
|
||||
# - thing.do_something() is treated as a call that can raise an exception while a file is open
|
||||
# - this call is treated as occurring after the open but not as being guarded by the with statement, as it is in the same basic block
|
||||
# - - this behavior has been changed fixing the FP
|
||||
@@ -306,11 +306,11 @@ def closed30(path):
|
||||
with open(path) as fp: # No longer spurious alert here.
|
||||
thing = NotWrapper(fp)
|
||||
|
||||
thing.do_something()
|
||||
thing.do_something()
|
||||
|
||||
|
||||
def closed31(path):
|
||||
with open(path) as fp:
|
||||
with open(path) as fp:
|
||||
data = fp.readline()
|
||||
data2 = fp.readline()
|
||||
|
||||
@@ -328,7 +328,7 @@ class Wrapper():
|
||||
def closed32(path):
|
||||
with open(path, "rb") as f: # No longer spurious alert here.
|
||||
wrap = Wrapper(f)
|
||||
# This resulted in an FP in a previous implementation,
|
||||
# This resulted in an FP in a previous implementation,
|
||||
# due to a check that an operation is lexically contained within a `with` block (with `expr.getParent*()`)
|
||||
# not detecting this case.
|
||||
return list(wrap.read())
|
||||
return list(wrap.read())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from flask import Flask, request, send_from_directory # $Source
|
||||
from flask import Flask, request, send_from_directory # $ Source
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ STATIC_DIR = "/server/static/"
|
||||
def download_file():
|
||||
filename = request.args.get('filename', '')
|
||||
# ok since `send_from_directory` ensure this stays within `STATIC_DIR`
|
||||
return send_from_directory(STATIC_DIR, filename) # $result=OK
|
||||
return send_from_directory(STATIC_DIR, filename) # $ result=OK
|
||||
|
||||
|
||||
# see https://flask.palletsprojects.com/en/1.1.x/api/#flask.send_from_directory
|
||||
|
||||
@@ -37,7 +37,7 @@ def safe_path_normpath():
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.normpath(os.path.join(STATIC_DIR, filename))
|
||||
if npath.startswith(STATIC_DIR):
|
||||
f = open(npath) # $result=OK
|
||||
f = open(npath) # $ result=OK
|
||||
|
||||
|
||||
@app.route("/path5")
|
||||
@@ -54,7 +54,7 @@ def safe_path_realpath():
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.realpath(os.path.join(STATIC_DIR, filename))
|
||||
if npath.startswith(STATIC_DIR):
|
||||
f = open(npath) # $result=OK
|
||||
f = open(npath) # $ result=OK
|
||||
|
||||
|
||||
@app.route("/path6")
|
||||
@@ -71,7 +71,7 @@ def safe_path_abspath():
|
||||
filename = request.args.get('filename', '')
|
||||
npath = os.path.abspath(os.path.join(STATIC_DIR, filename))
|
||||
if npath.startswith(STATIC_DIR):
|
||||
f = open(npath) # $result=OK
|
||||
f = open(npath) # $ result=OK
|
||||
|
||||
|
||||
@app.route("/abspath_tricky")
|
||||
|
||||
@@ -38,7 +38,7 @@ def normalize_then_check():
|
||||
x = source()
|
||||
y = normalize(x)
|
||||
if y.startswith("subfolder/"):
|
||||
open(y) # $result=OK
|
||||
open(y) # $ result=OK
|
||||
|
||||
|
||||
@app.route("/path")
|
||||
|
||||
@@ -20,7 +20,7 @@ def normalize_then_check():
|
||||
x = source()
|
||||
y = normalize(x) # <--- this call...
|
||||
if y.startswith("subfolder/"):
|
||||
open(y) # $result=OK
|
||||
open(y) # $ result=OK
|
||||
|
||||
|
||||
@app.route("/path")
|
||||
@@ -29,7 +29,7 @@ def normalize_check_normalize():
|
||||
y = normalize(x) # (...or this call...)
|
||||
if y.startswith("subfolder/"):
|
||||
z = normalize(y) # <--- ...can jump to here, resulting in FP
|
||||
open(z) # $result=OK
|
||||
open(z) # $ result=OK
|
||||
|
||||
|
||||
# The problem does not manifest if we simply define an alias
|
||||
@@ -42,4 +42,4 @@ def normalize_check_normalize_alias():
|
||||
y = normpath(x)
|
||||
if y.startswith("subfolder/"):
|
||||
z = normpath(y)
|
||||
open(z) # $result=OK
|
||||
open(z) # $ result=OK
|
||||
|
||||
@@ -10,27 +10,27 @@ app = Flask(__name__)
|
||||
def command_injection1():
|
||||
files = request.args.get('files', '')
|
||||
# Don't let files be `; rm -rf /`
|
||||
os.system("ls " + files) # $result=BAD
|
||||
os.system("ls " + files) # $ result=BAD
|
||||
|
||||
|
||||
@app.route("/command2")
|
||||
def command_injection2():
|
||||
files = request.args.get('files', '')
|
||||
# Don't let files be `; rm -rf /`
|
||||
subprocess.Popen("ls " + files, shell=True) # $result=BAD
|
||||
subprocess.Popen("ls " + files, shell=True) # $ result=BAD
|
||||
|
||||
|
||||
@app.route("/command3")
|
||||
def first_arg_injection():
|
||||
cmd = request.args.get('cmd', '')
|
||||
subprocess.Popen([cmd, "param1"]) # $result=BAD
|
||||
subprocess.Popen([cmd, "param1"]) # $ result=BAD
|
||||
|
||||
|
||||
@app.route("/other_cases")
|
||||
def others():
|
||||
files = request.args.get('files', '')
|
||||
# Don't let files be `; rm -rf /`
|
||||
os.popen("ls " + files) # $result=BAD
|
||||
os.popen("ls " + files) # $ result=BAD
|
||||
|
||||
|
||||
@app.route("/multiple")
|
||||
@@ -38,8 +38,8 @@ def multiple():
|
||||
command = request.args.get('command', '')
|
||||
# We should mark flow to both calls here, which conflicts with removing flow out of
|
||||
# a sink due to use-use flow.
|
||||
os.system(command) # $result=BAD
|
||||
os.system(command) # $result=BAD
|
||||
os.system(command) # $ result=BAD
|
||||
os.system(command) # $ result=BAD
|
||||
|
||||
|
||||
@app.route("/not-into-sink-impl")
|
||||
@@ -52,11 +52,11 @@ def not_into_sink_impl():
|
||||
subprocess.call implementation: https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/subprocess.py#L341
|
||||
"""
|
||||
command = request.args.get('command', '')
|
||||
os.system(command) # $result=BAD
|
||||
os.popen(command) # $result=BAD
|
||||
subprocess.call(command) # $result=BAD
|
||||
subprocess.check_call(command) # $result=BAD
|
||||
subprocess.run(command) # $result=BAD
|
||||
os.system(command) # $ result=BAD
|
||||
os.popen(command) # $ result=BAD
|
||||
subprocess.call(command) # $ result=BAD
|
||||
subprocess.check_call(command) # $ result=BAD
|
||||
subprocess.run(command) # $ result=BAD
|
||||
|
||||
|
||||
@app.route("/path-exists-not-sanitizer")
|
||||
@@ -70,11 +70,11 @@ def path_exists_not_sanitizer():
|
||||
"""
|
||||
path = request.args.get('path', '')
|
||||
if os.path.exists(path):
|
||||
os.system("ls " + path) # $result=BAD
|
||||
os.system("ls " + path) # $ result=BAD
|
||||
|
||||
|
||||
@app.route("/restricted-characters")
|
||||
def restricted_characters():
|
||||
path = request.args.get('path', '')
|
||||
if re.match(r'^[a-zA-Z0-9_-]+$', path):
|
||||
os.system("ls " + path) # $SPURIOUS: result=BAD
|
||||
os.system("ls " + path) # $ SPURIOUS: result=BAD
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import os
|
||||
import os
|
||||
|
||||
def unsafe_setup(name):
|
||||
os.system("ping " + name) # $result=OK - this is inside a setyp script, so it's fine.
|
||||
os.system("ping " + name) # $ result=OK - this is inside a setyp script, so it's fine.
|
||||
|
||||
@@ -2,52 +2,52 @@ import os
|
||||
import subprocess
|
||||
|
||||
def unsafe_shell_one(name):
|
||||
os.system("ping " + name) # $result=BAD
|
||||
os.system("ping " + name) # $ result=BAD
|
||||
|
||||
# f-strings
|
||||
os.system(f"ping {name}") # $result=BAD
|
||||
os.system(f"ping {name}") # $ result=BAD
|
||||
|
||||
# array.join
|
||||
os.system("ping " + " ".join(name)) # $result=BAD
|
||||
os.system("ping " + " ".join(name)) # $ result=BAD
|
||||
|
||||
# array.join, with a list
|
||||
os.system("ping " + " ".join([name])) # $result=BAD
|
||||
os.system("ping " + " ".join([name])) # $ result=BAD
|
||||
|
||||
# format, using .format
|
||||
os.system("ping {}".format(name)) # $result=BAD
|
||||
os.system("ping {}".format(name)) # $ result=BAD
|
||||
|
||||
# format, using %
|
||||
os.system("ping %s" % name) # $result=BAD
|
||||
os.system("ping %s" % name) # $ result=BAD
|
||||
|
||||
os.system(name) # OK - seems intentional.
|
||||
|
||||
import fabric
|
||||
|
||||
def facbric_stuff (name):
|
||||
def facbric_stuff (name):
|
||||
fabric.api.run("ping " + name, shell=False) # OK
|
||||
|
||||
fabric.api.run("ping " + name, shell=True) # $result=BAD
|
||||
fabric.api.run("ping " + name, shell=True) # $ result=BAD
|
||||
|
||||
def indirect(flag):
|
||||
def indirect(flag):
|
||||
fabric.api.run("ping " + name, shell=flag) # OK
|
||||
|
||||
indirect(False)
|
||||
|
||||
def subprocess_flag (name):
|
||||
def subprocess_flag (name):
|
||||
subprocess.run("ping " + name, shell=False) # OK - and nonsensical
|
||||
|
||||
subprocess.run("ping " + name, shell=True) # $result=BAD
|
||||
subprocess.run("ping " + name, shell=True) # $ result=BAD
|
||||
|
||||
def indirect(flag, x):
|
||||
subprocess.run("ping " + x, shell=flag) # $result=BAD
|
||||
def indirect(flag, x):
|
||||
subprocess.run("ping " + x, shell=flag) # $ result=BAD
|
||||
|
||||
indirect(True, name)
|
||||
|
||||
subprocess.Popen("ping " + name, shell=unknownValue) # OK - shell assumed to be False
|
||||
|
||||
def intentional(command):
|
||||
os.system("fish -ic " + command) # $result=OK - intentional
|
||||
def intentional(command):
|
||||
os.system("fish -ic " + command) # $ result=OK - intentional
|
||||
|
||||
import shlex
|
||||
def unsafe_shell_sanitized(name):
|
||||
os.system("ping " + shlex.quote(name)) # $result=OK - sanitized
|
||||
def unsafe_shell_sanitized(name):
|
||||
os.system("ping " + shlex.quote(name)) # $ result=OK - sanitized
|
||||
|
||||
@@ -5,8 +5,8 @@ app = Flask(__name__)
|
||||
@app.route("/test")
|
||||
def test():
|
||||
resp = make_response()
|
||||
resp.set_cookie("oauth", "value1") # $Alert[py/client-exposed-cookie]
|
||||
resp.set_cookie("oauth", "value2", secure=True) # $Alert[py/client-exposed-cookie]
|
||||
resp.set_cookie("oauth", "value2", httponly=True)
|
||||
resp.set_cookie("oauth", "value2", samesite="Strict") # $Alert[py/client-exposed-cookie]
|
||||
resp.set_cookie("oauth", "value2", httponly=True, samesite="None")
|
||||
resp.set_cookie("oauth", "value1") # $ Alert[py/client-exposed-cookie]
|
||||
resp.set_cookie("oauth", "value2", secure=True) # $ Alert[py/client-exposed-cookie]
|
||||
resp.set_cookie("oauth", "value2", httponly=True)
|
||||
resp.set_cookie("oauth", "value2", samesite="Strict") # $ Alert[py/client-exposed-cookie]
|
||||
resp.set_cookie("oauth", "value2", httponly=True, samesite="None")
|
||||
|
||||
@@ -5,11 +5,10 @@ app = Flask(__name__)
|
||||
@app.route("/test")
|
||||
def test(oauth_cookie_name):
|
||||
resp = make_response()
|
||||
resp.set_cookie("password", "value1")
|
||||
resp.set_cookie("authKey", "value2", samesite="Lax")
|
||||
resp.set_cookie("session_id", "value2", samesite="None") # $Alert[py/samesite-none-cookie]
|
||||
resp.set_cookie("oauth", "value2", secure=True, samesite="Strict")
|
||||
resp.set_cookie("oauth", "value2", httponly=True, samesite="Strict")
|
||||
resp.set_cookie(oauth_cookie_name, "value2", secure=True, samesite="None") # $Alert[py/samesite-none-cookie]
|
||||
resp.set_cookie("password", "value1")
|
||||
resp.set_cookie("authKey", "value2", samesite="Lax")
|
||||
resp.set_cookie("session_id", "value2", samesite="None") # $ Alert[py/samesite-none-cookie]
|
||||
resp.set_cookie("oauth", "value2", secure=True, samesite="Strict")
|
||||
resp.set_cookie("oauth", "value2", httponly=True, samesite="Strict")
|
||||
resp.set_cookie(oauth_cookie_name, "value2", secure=True, samesite="None") # $ Alert[py/samesite-none-cookie]
|
||||
resp.set_cookie("not_sensitive", "value2", samesite="None")
|
||||
|
||||
@@ -5,8 +5,8 @@ app = Flask(__name__)
|
||||
@app.route("/test")
|
||||
def test():
|
||||
resp = make_response()
|
||||
resp.set_cookie("authKey", "value1") # $Alert[py/insecure-cookie]
|
||||
resp.set_cookie("authKey", "value2", secure=True)
|
||||
resp.set_cookie("sessionID", "value2", httponly=True) # $Alert[py/insecure-cookie]
|
||||
resp.set_cookie("password", "value2", samesite="Strict") # $Alert[py/insecure-cookie]
|
||||
resp.set_cookie("notSensitive", "value3")
|
||||
resp.set_cookie("authKey", "value1") # $ Alert[py/insecure-cookie]
|
||||
resp.set_cookie("authKey", "value2", secure=True)
|
||||
resp.set_cookie("sessionID", "value2", httponly=True) # $ Alert[py/insecure-cookie]
|
||||
resp.set_cookie("password", "value2", samesite="Strict") # $ Alert[py/insecure-cookie]
|
||||
resp.set_cookie("notSensitive", "value3")
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
def bad1():
|
||||
results = []
|
||||
for x in range(10):
|
||||
def inner(): # $capturedVar=x
|
||||
def inner(): # $ capturedVar=x
|
||||
return x
|
||||
results.append(inner)
|
||||
return results
|
||||
|
||||
a = [lambda: i for i in range(1, 4)] # $capturedVar=i
|
||||
a = [lambda: i for i in range(1, 4)] # $ capturedVar=i
|
||||
for f in a:
|
||||
print(f())
|
||||
|
||||
@@ -46,30 +46,30 @@ def ok1():
|
||||
result += inner()
|
||||
return result
|
||||
|
||||
b = [lambda: i for i in range(1, 4) for j in range(1,5)] # $capturedVar=i
|
||||
c = [lambda: j for i in range(1, 4) for j in range(1,5)] # $capturedVar=j
|
||||
b = [lambda: i for i in range(1, 4) for j in range(1,5)] # $ capturedVar=i
|
||||
c = [lambda: j for i in range(1, 4) for j in range(1,5)] # $ capturedVar=j
|
||||
|
||||
s = {lambda: i for i in range(1, 4)} # $capturedVar=i
|
||||
s = {lambda: i for i in range(1, 4)} # $ capturedVar=i
|
||||
for f in s:
|
||||
print(f())
|
||||
|
||||
d = {i:lambda: i for i in range(1, 4)} # $capturedVar=i
|
||||
d = {i:lambda: i for i in range(1, 4)} # $ capturedVar=i
|
||||
for k, f in d.items():
|
||||
print(k, f())
|
||||
|
||||
#Generator expressions are sometimes OK, if they evaluate the iteration
|
||||
#Generator expressions are sometimes OK, if they evaluate the iteration
|
||||
#When the captured variable is used.
|
||||
#So technically this is a false positive, but it is extremely fragile
|
||||
#code, so I (Mark) think it is fine to report it as a violation.
|
||||
g = (lambda: i for i in range(1, 4)) # $capturedVar=i
|
||||
g = (lambda: i for i in range(1, 4)) # $ capturedVar=i
|
||||
for f in g:
|
||||
print(f())
|
||||
|
||||
#But not if evaluated eagerly
|
||||
l = list(lambda: i for i in range(1, 4)) # $capturedVar=i
|
||||
l = list(lambda: i for i in range(1, 4)) # $ capturedVar=i
|
||||
for f in l:
|
||||
print(f())
|
||||
|
||||
# This result is MISSING since the lambda is not detected to escape the loop
|
||||
def odasa4860(asset_ids):
|
||||
return dict((asset_id, filter(lambda c : c.asset_id == asset_id, xxx)) for asset_id in asset_ids) # $MISSING: capturedVar=asset_id
|
||||
return dict((asset_id, filter(lambda c : c.asset_id == asset_id, xxx)) for asset_id in asset_ids) # $ MISSING: capturedVar=asset_id
|
||||
|
||||
Reference in New Issue
Block a user