diff --git a/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll b/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll index 84f97d3f6ff6..6b55beada17b 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/PrintNode.qll @@ -1,7 +1,7 @@ import python import semmle.python.dataflow.new.DataFlow -string prettyExp(Expr e) { +string prettyExpr(Expr e) { not e instanceof Num and not e instanceof StrConst and not e instanceof Subscript and @@ -15,17 +15,41 @@ string prettyExp(Expr e) { e.(StrConst).getPrefix() + e.(StrConst).getText() + e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "") or - result = prettyExp(e.(Subscript).getObject()) + "[" + prettyExp(e.(Subscript).getIndex()) + "]" + result = prettyExpr(e.(Subscript).getObject()) + "[" + prettyExpr(e.(Subscript).getIndex()) + "]" or ( if exists(e.(Call).getAnArg()) or exists(e.(Call).getANamedArg()) - then result = prettyExp(e.(Call).getFunc()) + "(..)" - else result = prettyExp(e.(Call).getFunc()) + "()" + then result = prettyExpr(e.(Call).getFunc()) + "(..)" + else result = prettyExpr(e.(Call).getFunc()) + "()" ) or - result = prettyExp(e.(Attribute).getObject()) + "." + e.(Attribute).getName() + result = prettyExpr(e.(Attribute).getObject()) + "." + e.(Attribute).getName() } +/** + * Gets pretty-printed version of the DataFlow::Node `node` + */ +bindingset[node] string prettyNode(DataFlow::Node node) { - if exists(node.asExpr()) then result = prettyExp(node.asExpr()) else result = node.toString() + if exists(node.asExpr()) then result = prettyExpr(node.asExpr()) else result = node.toString() +} + +/** + * Gets pretty-printed version of the DataFlow::Node `node`, that is suitable for use + * with `TestUtilities.InlineExpectationsTest` (that is, no spaces unless required). + */ +bindingset[node] +string prettyNodeForInlineTest(DataFlow::Node node) { + exists(node.asExpr()) and + result = prettyExpr(node.asExpr()) + or + exists(Expr e | e = node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() | + // since PostUpdateNode both has space in the `[post ]` annotation, and does + // not pretty print the pre-update node, we do custom handling of this. + result = "[post]" + prettyExpr(e) + ) + or + not exists(node.asExpr()) and + not exists(Expr e | e = node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) and + result = node.toString() } diff --git a/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll b/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll index 32c04f3f3ab8..f0e57d667876 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll +++ b/python/ql/test/experimental/dataflow/tainttracking/TestTaintLib.qll @@ -46,6 +46,6 @@ query predicate test_taint(string arg_location, string test_res, string scope_na arg_location = arg.getLocation().toString() and test_res = test_res and scope_name = call.getScope().getName() and - repr = prettyExp(arg) + repr = prettyExpr(arg) ) } diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py deleted file mode 100644 index b1b0536b03b8..000000000000 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py +++ /dev/null @@ -1,53 +0,0 @@ -# Add taintlib to PATH so it can be imported during runtime without any hassle -import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__)))) -from taintlib import * - -# This has no runtime impact, but allows autocomplete to work -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ..taintlib import * - - -# Actual tests - -from io import StringIO -import json - -def test(): - print("\n# test") - ts = TAINTED_STRING - - encoded = json.dumps(ts) - - ensure_tainted( - encoded, # $ tainted - json.dumps(ts), # $ tainted - json.dumps(obj=ts), # $ tainted - json.loads(encoded), # $ tainted - json.loads(s=encoded), # $ tainted - ) - - # load/dump with file-like - tainted_filelike = StringIO() - json.dump(ts, tainted_filelike) - - tainted_filelike.seek(0) - ensure_tainted( - tainted_filelike, # $ tainted - json.load(tainted_filelike), # $ tainted - ) - - # load/dump with file-like using keyword-args - tainted_filelike = StringIO() - json.dump(obj=ts, fp=tainted_filelike) - - tainted_filelike.seek(0) - ensure_tainted( - tainted_filelike, # $ tainted - json.load(fp=tainted_filelike), # $ tainted - ) - - -# Make tests runable - -test() diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index 3800a4cd2731..6c97662feea6 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -2,19 +2,7 @@ import python import semmle.python.dataflow.new.DataFlow import semmle.python.Concepts import TestUtilities.InlineExpectationsTest - -string value_from_expr(Expr e) { - // TODO: This one is starting to look like `repr` predicate from TestTaintLib - result = - e.(StrConst).getPrefix() + e.(StrConst).getText() + - e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "") - or - result = e.(Name).getId() - or - not e instanceof StrConst and - not e instanceof Name and - result = e.toString() -} +import experimental.dataflow.TestUtil.PrintNode class SystemCommandExecutionTest extends InlineExpectationsTest { SystemCommandExecutionTest() { this = "SystemCommandExecutionTest" } @@ -27,7 +15,7 @@ class SystemCommandExecutionTest extends InlineExpectationsTest { command = sce.getCommand() and location = command.getLocation() and element = command.toString() and - value = value_from_expr(command.asExpr()) and + value = prettyNodeForInlineTest(command) and tag = "getCommand" ) } @@ -46,7 +34,7 @@ class DecodingTest extends InlineExpectationsTest { exists(DataFlow::Node data | location = data.getLocation() and element = data.toString() and - value = value_from_expr(data.asExpr()) and + value = prettyNodeForInlineTest(data) and ( data = d.getAnInput() and tag = "decodeInput" @@ -84,7 +72,7 @@ class EncodingTest extends InlineExpectationsTest { exists(DataFlow::Node data | location = data.getLocation() and element = data.toString() and - value = value_from_expr(data.asExpr()) and + value = prettyNodeForInlineTest(data) and ( data = e.getAnInput() and tag = "encodeInput" @@ -117,7 +105,7 @@ class CodeExecutionTest extends InlineExpectationsTest { code = ce.getCode() and location = code.getLocation() and element = code.toString() and - value = value_from_expr(code.asExpr()) and + value = prettyNodeForInlineTest(code) and tag = "getCode" ) } @@ -135,7 +123,7 @@ class SqlExecutionTest extends InlineExpectationsTest { sql = e.getSql() and location = e.getLocation() and element = sql.toString() and - value = value_from_expr(sql.asExpr()) and + value = prettyNodeForInlineTest(sql) and tag = "getSql" ) } @@ -218,7 +206,7 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest { exists(HTTP::Server::HttpResponse response | location = response.getLocation() and element = response.toString() and - value = value_from_expr(response.getBody().asExpr()) and + value = prettyNodeForInlineTest(response.getBody()) and tag = "responseBody" ) or @@ -257,7 +245,7 @@ class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest { exists(HTTP::Server::HttpRedirectResponse redirect | location = redirect.getLocation() and element = redirect.toString() and - value = value_from_expr(redirect.getRedirectLocation().asExpr()) and + value = prettyNodeForInlineTest(redirect.getRedirectLocation()) and tag = "redirectLocation" ) ) @@ -275,7 +263,7 @@ class FileSystemAccessTest extends InlineExpectationsTest { path = a.getAPathArgument() and location = a.getLocation() and element = path.toString() and - value = value_from_expr(path.asExpr()) and + value = prettyNodeForInlineTest(path) and tag = "getAPathArgument" ) } @@ -309,7 +297,7 @@ class SafeAccessCheckTest extends InlineExpectationsTest { location = c.getLocation() and ( element = checks.toString() and - value = value_from_expr(checks.asExpr()) and + value = prettyNodeForInlineTest(checks) and tag = "checks" or element = branch.toString() and diff --git a/python/ql/test/experimental/meta/InlineTaintTest.qll b/python/ql/test/experimental/meta/InlineTaintTest.qll index dd0865adc4b4..da1f084f97dd 100644 --- a/python/ql/test/experimental/meta/InlineTaintTest.qll +++ b/python/ql/test/experimental/meta/InlineTaintTest.qll @@ -58,7 +58,7 @@ class InlineTaintTest extends InlineExpectationsTest { exists(DataFlow::Node sink | any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and location = sink.getLocation() and - element = prettyExp(sink.asExpr()) and + element = prettyExpr(sink.asExpr()) and value = "" and tag = "tainted" ) @@ -84,7 +84,7 @@ query predicate untaintedArgumentToEnsureTaintedNotMarkedAsMissing( error = "ERROR, you should add `# $ MISSING: tainted` annotation" and exists(DataFlow::Node sink | sink = shouldBeTainted() and - element = prettyExp(sink.asExpr()) and + element = prettyExpr(sink.asExpr()) and not any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and location = sink.getLocation() and not exists(FalseNegativeExpectation missingResult | diff --git a/python/ql/test/library-tests/frameworks/dill/Decoding.py b/python/ql/test/library-tests/frameworks/dill/Decoding.py index a02b013104b6..49eb551af046 100644 --- a/python/ql/test/library-tests/frameworks/dill/Decoding.py +++ b/python/ql/test/library-tests/frameworks/dill/Decoding.py @@ -1,3 +1,3 @@ import dill -dill.loads(payload) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=dill decodeMayExecuteInput +dill.loads(payload) # $decodeInput=payload decodeOutput=dill.loads(..) decodeFormat=dill decodeMayExecuteInput diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py b/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py index feeb4d48b887..c7da2f35b6ac 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/response_test.py @@ -74,20 +74,20 @@ def get_redirect_url(self, foo): # $ requestHandler routedParameter=foo # Ensure that simple subclasses are still vuln to XSS def xss__not_found(request): - return HttpResponseNotFound(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=Attribute() + return HttpResponseNotFound(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=request.GET.get(..) # Ensure we still have an XSS sink when manually setting the content_type to HTML def xss__manual_response_type(request): - return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") # $HttpResponse mimetype=text/html responseBody=Attribute() + return HttpResponse(request.GET.get("name"), content_type="text/html; charset=utf-8") # $HttpResponse mimetype=text/html responseBody=request.GET.get(..) def xss__write(request): response = HttpResponse() # $HttpResponse mimetype=text/html - response.write(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=Attribute() + response.write(request.GET.get("name")) # $HttpResponse mimetype=text/html responseBody=request.GET.get(..) # This is safe but probably a bug if the argument to `write` is not a result of `json.dumps` or similar. def safe__write_json(request): response = JsonResponse() # $HttpResponse mimetype=application/json - response.write(request.GET.get("name")) # $HttpResponse mimetype=application/json responseBody=Attribute() + response.write(request.GET.get("name")) # $HttpResponse mimetype=application/json responseBody=request.GET.get(..) # Ensure manual subclasses are vulnerable class CustomResponse(HttpResponse): @@ -95,7 +95,7 @@ def __init__(self, banner, content, *args, **kwargs): super().__init__(content, *args, content_type="text/html", **kwargs) def xss__custom_response(request): - return CustomResponse("ACME Responses", request.GET("name")) # $HttpResponse MISSING: mimetype=text/html responseBody=Attribute() SPURIOUS: responseBody="ACME Responses" + return CustomResponse("ACME Responses", request.GET("name")) # $HttpResponse MISSING: mimetype=text/html responseBody=request.GET.get(..) SPURIOUS: responseBody="ACME Responses" class CustomJsonResponse(JsonResponse): def __init__(self, banner, content, *args, **kwargs): diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py index 1339a14312c8..5343182c1c9c 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/settings.py @@ -13,7 +13,7 @@ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent #$ getAPathArgument=Path() +BASE_DIR = Path(__file__).resolve().parent.parent #$ getAPathArgument=Path(..) # Quick-start development settings - unsuitable for production diff --git a/python/ql/test/library-tests/frameworks/idna/taint_test.py b/python/ql/test/library-tests/frameworks/idna/taint_test.py index 61b9dad166c4..3b20b9e678e7 100644 --- a/python/ql/test/library-tests/frameworks/idna/taint_test.py +++ b/python/ql/test/library-tests/frameworks/idna/taint_test.py @@ -5,9 +5,9 @@ def test_idna(): tb = TAINTED_BYTES ensure_tainted( - idna.encode(ts), # $ tainted encodeInput=ts encodeOutput=Attribute() encodeFormat=IDNA - idna.encode(s=ts), # $ tainted encodeInput=ts encodeOutput=Attribute() encodeFormat=IDNA + idna.encode(ts), # $ tainted encodeInput=ts encodeOutput=idna.encode(..) encodeFormat=IDNA + idna.encode(s=ts), # $ tainted encodeInput=ts encodeOutput=idna.encode(..) encodeFormat=IDNA - idna.decode(tb), # $ tainted decodeInput=tb decodeOutput=Attribute() decodeFormat=IDNA - idna.decode(s=tb), # $ tainted decodeInput=tb decodeOutput=Attribute() decodeFormat=IDNA + idna.decode(tb), # $ tainted decodeInput=tb decodeOutput=idna.decode(..) decodeFormat=IDNA + idna.decode(s=tb), # $ tainted decodeInput=tb decodeOutput=idna.decode(..) decodeFormat=IDNA ) diff --git a/python/ql/test/library-tests/frameworks/simplejson/taint_test.py b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py index 6f5d45b0b335..59bec5d6978c 100644 --- a/python/ql/test/library-tests/frameworks/simplejson/taint_test.py +++ b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py @@ -5,14 +5,14 @@ def test(): ts = TAINTED_STRING tainted_obj = {"foo": ts} - encoded = simplejson.dumps(tainted_obj) # $ encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + encoded = simplejson.dumps(tainted_obj) # $ encodeOutput=simplejson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj ensure_tainted( encoded, # $ tainted - simplejson.dumps(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj - simplejson.dumps(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj - simplejson.loads(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded - simplejson.loads(s=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + simplejson.dumps(tainted_obj), # $ tainted encodeOutput=simplejson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj + simplejson.dumps(obj=tainted_obj), # $ tainted encodeOutput=simplejson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj + simplejson.loads(encoded), # $ tainted decodeOutput=simplejson.loads(..) decodeFormat=JSON decodeInput=encoded + simplejson.loads(s=encoded), # $ tainted decodeOutput=simplejson.loads(..) decodeFormat=JSON decodeInput=encoded ) # load/dump with file-like @@ -22,7 +22,7 @@ def test(): tainted_filelike.seek(0) ensure_tainted( tainted_filelike, # $ MISSING: tainted - simplejson.load(tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted + simplejson.load(tainted_filelike), # $ decodeOutput=simplejson.load(..) decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted ) # load/dump with file-like using keyword-args @@ -32,7 +32,7 @@ def test(): tainted_filelike.seek(0) ensure_tainted( tainted_filelike, # $ MISSING: tainted - simplejson.load(fp=tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted + simplejson.load(fp=tainted_filelike), # $ decodeOutput=simplejson.load(..) decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted ) # To make things runable diff --git a/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py b/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py index 886932495f25..11cca46bdf7c 100644 --- a/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py +++ b/python/ql/test/library-tests/frameworks/stdlib-py3/Decoding.py @@ -1,6 +1,6 @@ import base64 # TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py -base64.a85decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Ascii85 -base64.b85decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base85 -base64.decodebytes(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64 +base64.a85decode(payload) # $ decodeInput=payload decodeOutput=base64.a85decode(..) decodeFormat=Ascii85 +base64.b85decode(payload) # $ decodeInput=payload decodeOutput=base64.b85decode(..) decodeFormat=Base85 +base64.decodebytes(payload) # $ decodeInput=payload decodeOutput=base64.decodebytes(..) decodeFormat=Base64 diff --git a/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py b/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py index 79fd3a2cd76b..7aea6a5e579b 100644 --- a/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py +++ b/python/ql/test/library-tests/frameworks/stdlib-py3/Encoding.py @@ -1,6 +1,6 @@ import base64 # TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/test_string.py -base64.a85encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Ascii85 -base64.b85encode(bs)# $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base85 -base64.encodebytes(bs)# $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64 +base64.a85encode(bs) # $ encodeInput=bs encodeOutput=base64.a85encode(..) encodeFormat=Ascii85 +base64.b85encode(bs)# $ encodeInput=bs encodeOutput=base64.b85encode(..) encodeFormat=Base85 +base64.encodebytes(bs)# $ encodeInput=bs encodeOutput=base64.encodebytes(..) encodeFormat=Base64 diff --git a/python/ql/test/library-tests/frameworks/stdlib/Decoding.py b/python/ql/test/library-tests/frameworks/stdlib/Decoding.py index baf97c90c4df..5d157a61f6e7 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/Decoding.py +++ b/python/ql/test/library-tests/frameworks/stdlib/Decoding.py @@ -2,14 +2,14 @@ import marshal import base64 -pickle.loads(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=pickle decodeMayExecuteInput -marshal.loads(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=marshal decodeMayExecuteInput +pickle.loads(payload) # $ decodeInput=payload decodeOutput=pickle.loads(..) decodeFormat=pickle decodeMayExecuteInput +marshal.loads(payload) # $ decodeInput=payload decodeOutput=marshal.loads(..) decodeFormat=marshal decodeMayExecuteInput # TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py -base64.b64decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64 -base64.standard_b64decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64 -base64.urlsafe_b64decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64 -base64.b32decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base32 -base64.b16decode(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base16 +base64.b64decode(payload) # $ decodeInput=payload decodeOutput=base64.b64decode(..) decodeFormat=Base64 +base64.standard_b64decode(payload) # $ decodeInput=payload decodeOutput=base64.standard_b64decode(..) decodeFormat=Base64 +base64.urlsafe_b64decode(payload) # $ decodeInput=payload decodeOutput=base64.urlsafe_b64decode(..) decodeFormat=Base64 +base64.b32decode(payload) # $ decodeInput=payload decodeOutput=base64.b32decode(..) decodeFormat=Base32 +base64.b16decode(payload) # $ decodeInput=payload decodeOutput=base64.b16decode(..) decodeFormat=Base16 # deprecated since Python 3.1, but still works -base64.decodestring(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=Base64 +base64.decodestring(payload) # $ decodeInput=payload decodeOutput=base64.decodestring(..) decodeFormat=Base64 diff --git a/python/ql/test/library-tests/frameworks/stdlib/Encoding.py b/python/ql/test/library-tests/frameworks/stdlib/Encoding.py index 9a6923ce11f9..2ad51e66fc1b 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/Encoding.py +++ b/python/ql/test/library-tests/frameworks/stdlib/Encoding.py @@ -2,14 +2,14 @@ import marshal import base64 -pickle.dumps(obj) # $ MISSING: f-:encodeInput=obj f-:encodeOutput=Attribute() f-:encodeFormat=pickle f-:encodeMayExecuteInput -marshal.dumps(obj) # $ MISSING: f-:encodeInput=obj f-:encodeOutput=Attribute() f-:encodeFormat=marshal f-:encodeMayExecuteInput +pickle.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=pickle.dumps(..) encodeFormat=pickle encodeMayExecuteInput +marshal.dumps(obj) # $ MISSING: encodeInput=obj encodeOutput=marshal.dumps(..) encodeFormat=marshal encodeMayExecuteInput # TODO: These tests should be merged with python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py -base64.b64encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64 -base64.standard_b64encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64 -base64.urlsafe_b64encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64 -base64.b32encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base32 -base64.b16encode(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base16 +base64.b64encode(bs) # $ encodeInput=bs encodeOutput=base64.b64encode(..) encodeFormat=Base64 +base64.standard_b64encode(bs) # $ encodeInput=bs encodeOutput=base64.standard_b64encode(..) encodeFormat=Base64 +base64.urlsafe_b64encode(bs) # $ encodeInput=bs encodeOutput=base64.urlsafe_b64encode(..) encodeFormat=Base64 +base64.b32encode(bs) # $ encodeInput=bs encodeOutput=base64.b32encode(..) encodeFormat=Base32 +base64.b16encode(bs) # $ encodeInput=bs encodeOutput=base64.b16encode(..) encodeFormat=Base16 # deprecated since Python 3.1, but still works -base64.encodestring(bs) # $ encodeInput=bs encodeOutput=Attribute() encodeFormat=Base64 +base64.encodestring(bs) # $ encodeInput=bs encodeOutput=base64.encodestring(..) encodeFormat=Base64 diff --git a/python/ql/test/library-tests/frameworks/stdlib/test_json.py b/python/ql/test/library-tests/frameworks/stdlib/test_json.py new file mode 100644 index 000000000000..113065c77fd1 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/stdlib/test_json.py @@ -0,0 +1,40 @@ +from io import StringIO +import json + +def test(): + print("\n# test") + ts = TAINTED_STRING + + encoded = json.dumps(ts) # $ encodeOutput=json.dumps(..) encodeFormat=JSON encodeInput=ts + + ensure_tainted( + encoded, # $ tainted + json.dumps(ts), # $ tainted encodeOutput=json.dumps(..) encodeFormat=JSON encodeInput=ts + json.dumps(obj=ts), # $ tainted encodeOutput=json.dumps(..) encodeFormat=JSON encodeInput=ts + json.loads(encoded), # $ tainted decodeOutput=json.loads(..) decodeFormat=JSON decodeInput=encoded + json.loads(s=encoded), # $ tainted decodeOutput=json.loads(..) decodeFormat=JSON decodeInput=encoded + ) + + # load/dump with file-like + tainted_filelike = StringIO() + json.dump(ts, tainted_filelike) # $ encodeOutput=[post]tainted_filelike encodeFormat=JSON encodeInput=ts + + tainted_filelike.seek(0) + ensure_tainted( + tainted_filelike, # $ tainted + json.load(tainted_filelike), # $ tainted decodeOutput=json.load(..) decodeFormat=JSON decodeInput=tainted_filelike + ) + + # load/dump with file-like using keyword-args + tainted_filelike = StringIO() + json.dump(obj=ts, fp=tainted_filelike) # $ encodeOutput=[post]tainted_filelike encodeFormat=JSON encodeInput=ts + + tainted_filelike.seek(0) + ensure_tainted( + tainted_filelike, # $ tainted + json.load(fp=tainted_filelike), # $ tainted decodeOutput=json.load(..) decodeFormat=JSON decodeInput=tainted_filelike + ) + + +# Make tests runable +test() diff --git a/python/ql/test/library-tests/frameworks/ujson/taint_test.py b/python/ql/test/library-tests/frameworks/ujson/taint_test.py index 2c9655183933..49423625a58d 100644 --- a/python/ql/test/library-tests/frameworks/ujson/taint_test.py +++ b/python/ql/test/library-tests/frameworks/ujson/taint_test.py @@ -5,19 +5,19 @@ def test(): ts = TAINTED_STRING tainted_obj = {"foo": ts} - encoded = ujson.dumps(tainted_obj) # $ encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + encoded = ujson.dumps(tainted_obj) # $ encodeOutput=ujson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj ensure_tainted( encoded, # $ tainted - ujson.dumps(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj - ujson.dumps(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj - ujson.loads(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded - ujson.loads(obj=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded - - ujson.encode(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj - ujson.encode(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj - ujson.decode(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded - ujson.decode(obj=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + ujson.dumps(tainted_obj), # $ tainted encodeOutput=ujson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj + ujson.dumps(obj=tainted_obj), # $ tainted encodeOutput=ujson.dumps(..) encodeFormat=JSON encodeInput=tainted_obj + ujson.loads(encoded), # $ tainted decodeOutput=ujson.loads(..) decodeFormat=JSON decodeInput=encoded + ujson.loads(obj=encoded), # $ tainted decodeOutput=ujson.loads(..) decodeFormat=JSON decodeInput=encoded + + ujson.encode(tainted_obj), # $ tainted encodeOutput=ujson.encode(..) encodeFormat=JSON encodeInput=tainted_obj + ujson.encode(obj=tainted_obj), # $ tainted encodeOutput=ujson.encode(..) encodeFormat=JSON encodeInput=tainted_obj + ujson.decode(encoded), # $ tainted decodeOutput=ujson.decode(..) decodeFormat=JSON decodeInput=encoded + ujson.decode(obj=encoded), # $ tainted decodeOutput=ujson.decode(..) decodeFormat=JSON decodeInput=encoded ) # load/dump with file-like @@ -27,7 +27,7 @@ def test(): tainted_filelike.seek(0) ensure_tainted( tainted_filelike, # $ MISSING: tainted - ujson.load(tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted + ujson.load(tainted_filelike), # $ decodeOutput=ujson.load(..) decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted ) # load/dump with file-like using keyword-args does not work in `ujson` diff --git a/python/ql/test/library-tests/frameworks/yaml/Decoding.py b/python/ql/test/library-tests/frameworks/yaml/Decoding.py index 987ea76552a7..b3cc627883db 100644 --- a/python/ql/test/library-tests/frameworks/yaml/Decoding.py +++ b/python/ql/test/library-tests/frameworks/yaml/Decoding.py @@ -1,37 +1,37 @@ import yaml # Unsafe: -yaml.load(payload) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput -yaml.load(payload, yaml.Loader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput -yaml.unsafe_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput -yaml.full_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput +yaml.load(payload) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput +yaml.load(payload, yaml.Loader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput +yaml.unsafe_load(payload) # $ decodeInput=payload decodeOutput=yaml.unsafe_load(..) decodeFormat=YAML decodeMayExecuteInput +yaml.full_load(payload) # $ decodeInput=payload decodeOutput=yaml.full_load(..) decodeFormat=YAML decodeMayExecuteInput # Safe: -yaml.load(payload, yaml.SafeLoader) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML -yaml.load(payload, Loader=yaml.SafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML -yaml.load(payload, yaml.BaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML -yaml.safe_load(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML +yaml.load(payload, yaml.SafeLoader) # $ decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML +yaml.load(payload, Loader=yaml.SafeLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML +yaml.load(payload, yaml.BaseLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML +yaml.safe_load(payload) # $ decodeInput=payload decodeOutput=yaml.safe_load(..) decodeFormat=YAML ################################################################################ # load_all variants ################################################################################ # Unsafe: -yaml.load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput -yaml.unsafe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput -yaml.full_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput +yaml.load_all(payload) # $ decodeInput=payload decodeOutput=yaml.load_all(..) decodeFormat=YAML decodeMayExecuteInput +yaml.unsafe_load_all(payload) # $ decodeInput=payload decodeOutput=yaml.unsafe_load_all(..) decodeFormat=YAML decodeMayExecuteInput +yaml.full_load_all(payload) # $ decodeInput=payload decodeOutput=yaml.full_load_all(..) decodeFormat=YAML decodeMayExecuteInput # Safe: -yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML +yaml.safe_load_all(payload) # $ decodeInput=payload decodeOutput=yaml.safe_load_all(..) decodeFormat=YAML ################################################################################ # C-based loaders with `libyaml` ################################################################################ # Unsafe: -yaml.load(payload, yaml.CLoader) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput -yaml.load(payload, yaml.CFullLoader) # $ decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML decodeMayExecuteInput +yaml.load(payload, yaml.CLoader) # $ decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput +yaml.load(payload, yaml.CFullLoader) # $ decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML decodeMayExecuteInput # Safe: -yaml.load(payload, yaml.CSafeLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML -yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=Attribute() decodeFormat=YAML +yaml.load(payload, yaml.CSafeLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML +yaml.load(payload, yaml.CBaseLoader) # $decodeInput=payload decodeOutput=yaml.load(..) decodeFormat=YAML