From fa6abea465bcd8f14f7c14ecb3402f48a0bb1adb Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 9 Jun 2021 12:13:29 +0200 Subject: [PATCH] Python: Add modeling of jmespath --- docs/codeql/support/reusables/frameworks.rst | 1 + .../2021-06-09-add-jmespath-modeling.md | 2 ++ python/ql/src/semmle/python/Frameworks.qll | 1 + .../src/semmle/python/frameworks/Jmespath.qll | 35 +++++++++++++++++++ .../frameworks/jmespath/taint_test.py | 8 ++--- 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 python/change-notes/2021-06-09-add-jmespath-modeling.md create mode 100644 python/ql/src/semmle/python/frameworks/Jmespath.qll diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index be9949aad77..58764bfe39a 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -162,6 +162,7 @@ Python built-in support fabric, Utility library invoke, Utility library idna, Utility library + jmespath, Utility library mysql-connector-python, Database MySQLdb, Database psycopg2, Database diff --git a/python/change-notes/2021-06-09-add-jmespath-modeling.md b/python/change-notes/2021-06-09-add-jmespath-modeling.md new file mode 100644 index 00000000000..a26699a79b2 --- /dev/null +++ b/python/change-notes/2021-06-09-add-jmespath-modeling.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added modeling of the PyPI package `jmespath`. diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll index 3115c3ffac6..3735f5bcf30 100644 --- a/python/ql/src/semmle/python/Frameworks.qll +++ b/python/ql/src/semmle/python/Frameworks.qll @@ -12,6 +12,7 @@ private import semmle.python.frameworks.Fabric private import semmle.python.frameworks.Flask private import semmle.python.frameworks.Idna private import semmle.python.frameworks.Invoke +private import semmle.python.frameworks.Jmespath private import semmle.python.frameworks.MysqlConnectorPython private import semmle.python.frameworks.MySQLdb private import semmle.python.frameworks.Psycopg2 diff --git a/python/ql/src/semmle/python/frameworks/Jmespath.qll b/python/ql/src/semmle/python/frameworks/Jmespath.qll new file mode 100644 index 00000000000..6d9f4aa4fd9 --- /dev/null +++ b/python/ql/src/semmle/python/frameworks/Jmespath.qll @@ -0,0 +1,35 @@ +/** + * Provides classes modeling security-relevant aspects of the `jmespath` PyPI package. + * See https://pypi.org/project/jmespath/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.Concepts +private import semmle.python.ApiGraphs + +/** + * Provides models for the `jmespath` PyPI package. + * See https://pypi.org/project/jmespath/. + */ +private module Jmespath { + class JmespathAdditionalTaintSteps extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(DataFlow::CallCfgNode call | + call = API::moduleImport("jmespath").getMember("search").getACall() and + nodeFrom in [call.getArg(1), call.getArgByName("data")] and + nodeTo = call + or + call = + API::moduleImport("jmespath") + .getMember("compile") + .getReturn() + .getMember("search") + .getACall() and + nodeFrom in [call.getArg(0), call.getArgByName("value")] and + nodeTo = call + ) + } + } +} diff --git a/python/ql/test/library-tests/frameworks/jmespath/taint_test.py b/python/ql/test/library-tests/frameworks/jmespath/taint_test.py index 6386cb8e2a7..4af680cbbe2 100644 --- a/python/ql/test/library-tests/frameworks/jmespath/taint_test.py +++ b/python/ql/test/library-tests/frameworks/jmespath/taint_test.py @@ -6,11 +6,11 @@ def test_idna(): expression = jmespath.compile("foo.bar") ensure_tainted( - jmespath.search("foo.bar", data), # $ MISSING: tainted - jmespath.search("foo.bar", data=data), # $ MISSING: tainted + jmespath.search("foo.bar", data), # $ tainted + jmespath.search("foo.bar", data=data), # $ tainted - expression.search(data), # $ MISSING: tainted - expression.search(value=data) # $ MISSING: tainted + expression.search(data), # $ tainted + expression.search(value=data) # $ tainted ) # since ```jmespath.search("{wat: `foo`}", {})``` works (and outputs a dictionary),