diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst index a244bd00f547..30888f7b6092 100644 --- a/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst +++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst @@ -427,7 +427,7 @@ Kinds Source kinds ~~~~~~~~~~~~ -- **remote**: A generic source of remote flow. Most taint-tracking queries will use such a source. Currently this is the only supported source kind. +See documentation below for :ref:`Threat models `. Sink kinds ~~~~~~~~~~ @@ -449,3 +449,10 @@ Summary kinds - **taint**: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well. - **value**: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved. + +.. _threat-models-python: + +Threat models +------------- + +.. include:: ../reusables/threat-model-description.rst diff --git a/docs/codeql/reusables/beta-note-threat-models.rst b/docs/codeql/reusables/beta-note-threat-models.rst index 80c97d93376c..9fcca40975a1 100644 --- a/docs/codeql/reusables/beta-note-threat-models.rst +++ b/docs/codeql/reusables/beta-note-threat-models.rst @@ -2,4 +2,4 @@ Note - Threat models are currently in beta and subject to change. During the beta, threat models are supported only by Java and C# analysis. + Threat models are currently in beta and subject to change. During the beta, threat models are supported only by Java, C# and Python analysis. diff --git a/python/ql/lib/change-notes/2024-08-16-threat-models.md b/python/ql/lib/change-notes/2024-08-16-threat-models.md new file mode 100644 index 000000000000..ba01e6f6fbda --- /dev/null +++ b/python/ql/lib/change-notes/2024-08-16-threat-models.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Added support for custom threat-models, which can be used in most of our taint-tracking queries, see our [documentation](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#extending-codeql-coverage-with-threat-models) for more details. diff --git a/python/ql/lib/ext/default-threat-models-fixup.model.yml b/python/ql/lib/ext/default-threat-models-fixup.model.yml new file mode 100644 index 000000000000..cc1cb20517ec --- /dev/null +++ b/python/ql/lib/ext/default-threat-models-fixup.model.yml @@ -0,0 +1,8 @@ +extensions: + - addsTo: + pack: codeql/threat-models + extensible: threatModelConfiguration + data: + # Since responses are enabled by default in the shared threat-models configuration, + # we need to disable it here to keep existing behavior for the python analysis. + - ["response", false, -2147483647] diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 81d09c13b5d3..36b1f7242be0 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -9,10 +9,12 @@ dependencies: codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/regex: ${workspace} + codeql/threat-models: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} codeql/xml: ${workspace} codeql/yaml: ${workspace} dataExtensions: - semmle/python/frameworks/**/*.model.yml + - ext/*.model.yml warnOnImplicitThis: true diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index 75b884a9dd4d..a5a7b8f5b9ac 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -10,6 +10,62 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import semmle.python.Frameworks private import semmle.python.security.internal.EncryptionKeySizes +private import codeql.threatmodels.ThreatModels + +/** + * A data flow source, for a specific threat-model. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `ThreatModelSource::Range` instead. + */ +class ThreatModelSource extends DataFlow::Node instanceof ThreatModelSource::Range { + /** + * Gets a string that represents the source kind with respect to threat modeling. + * + * See + * - https://github.com/github/codeql/blob/main/docs/codeql/reusables/threat-model-description.rst + * - https://github.com/github/codeql/blob/main/shared/threat-models/ext/threat-model-grouping.model.yml + */ + string getThreatModel() { result = super.getThreatModel() } + + /** Gets a string that describes the type of this threat-model source. */ + string getSourceType() { result = super.getSourceType() } +} + +/** Provides a class for modeling new sources for specific threat-models. */ +module ThreatModelSource { + /** + * A data flow source, for a specific threat-model. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `ThreatModelSource` instead. + */ + abstract class Range extends DataFlow::Node { + /** + * Gets a string that represents the source kind with respect to threat modeling. + * + * See + * - https://github.com/github/codeql/blob/main/docs/codeql/reusables/threat-model-description.rst + * - https://github.com/github/codeql/blob/main/shared/threat-models/ext/threat-model-grouping.model.yml + */ + abstract string getThreatModel(); + + /** Gets a string that describes the type of this threat-model source. */ + abstract string getSourceType(); + } +} + +/** + * A data flow source that is enabled in the current threat model configuration. + */ +class ActiveThreatModelSource extends DataFlow::Node { + ActiveThreatModelSource() { + exists(string kind | + currentThreatModel(kind) and + this.(ThreatModelSource).getThreatModel() = kind + ) + } +} /** * A data-flow node that executes an operating system command, diff --git a/python/ql/lib/semmle/python/dataflow/new/RemoteFlowSources.qll b/python/ql/lib/semmle/python/dataflow/new/RemoteFlowSources.qll index 4ad0aee1f313..8975b967c813 100644 --- a/python/ql/lib/semmle/python/dataflow/new/RemoteFlowSources.qll +++ b/python/ql/lib/semmle/python/dataflow/new/RemoteFlowSources.qll @@ -15,10 +15,7 @@ private import semmle.python.Concepts * Extend this class to refine existing API models. If you want to model new APIs, * extend `RemoteFlowSource::Range` instead. */ -class RemoteFlowSource extends DataFlow::Node instanceof RemoteFlowSource::Range { - /** Gets a string that describes the type of this remote flow source. */ - string getSourceType() { result = super.getSourceType() } -} +class RemoteFlowSource extends ThreatModelSource instanceof RemoteFlowSource::Range { } /** Provides a class for modeling new sources of remote user input. */ module RemoteFlowSource { @@ -28,8 +25,7 @@ module RemoteFlowSource { * Extend this class to model new APIs. If you want to refine existing API models, * extend `RemoteFlowSource` instead. */ - abstract class Range extends DataFlow::Node { - /** Gets a string that describes the type of this remote flow source. */ - abstract string getSourceType(); + abstract class Range extends ThreatModelSource::Range { + override string getThreatModel() { result = "remote" } } } diff --git a/python/ql/lib/semmle/python/frameworks/PEP249.qll b/python/ql/lib/semmle/python/frameworks/PEP249.qll index 2425f4514f84..1eecc12b3d1e 100644 --- a/python/ql/lib/semmle/python/frameworks/PEP249.qll +++ b/python/ql/lib/semmle/python/frameworks/PEP249.qll @@ -81,6 +81,24 @@ module PEP249 { } } + /** A call to a method that fetches rows from a previous execution. */ + private class FetchMethodCall extends ThreatModelSource::Range, API::CallNode { + FetchMethodCall() { + exists(API::Node start | + start instanceof DatabaseCursor or start instanceof DatabaseConnection + | + // note: since we can't currently provide accesspaths for sources, these are all + // lumped together, although clearly the fetchmany/fetchall returns a + // list/iterable with rows. + this = start.getMember(["fetchone", "fetchmany", "fetchall"]).getACall() + ) + } + + override string getThreatModel() { result = "database" } + + override string getSourceType() { result = "cursor.fetch*()" } + } + // --------------------------------------------------------------------------- // asyncio implementations // --------------------------------------------------------------------------- diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.model.yml b/python/ql/lib/semmle/python/frameworks/Stdlib.model.yml new file mode 100644 index 000000000000..53d918d07ac3 --- /dev/null +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.model.yml @@ -0,0 +1,29 @@ +extensions: + - addsTo: + pack: codeql/python-all + extensible: sourceModel + data: + - ['os', 'Member[getenv].ReturnValue', 'environment'] + - ['os', 'Member[getenvb].ReturnValue', 'environment'] + - ['os', 'Member[environ]', 'environment'] + - ['os', 'Member[environb]', 'environment'] + - ['posix', 'Member[environ]', 'environment'] + + - ['sys', 'Member[argv]', 'commandargs'] + - ['sys', 'Member[orig_argv]', 'commandargs'] + + - ['sys', 'Member[stdin]', 'stdin'] + - ['builtins', 'Member[input].ReturnValue', 'stdin'] + - ['builtins', 'Member[raw_input].ReturnValue', 'stdin'] # python 2 only + + + # if no argument is given, the default is to use sys.argv[1:] + - ['argparse.ArgumentParser', 'Member[parse_args,parse_known_args].WithArity[0].ReturnValue', 'commandargs'] + + - ['os', 'Member[read].ReturnValue', 'file'] + - addsTo: + pack: codeql/python-all + extensible: summaryModel + data: + - ['argparse.ArgumentParser', 'Member[parse_args,parse_known_args]', 'Argument[0,args:]', 'ReturnValue', 'taint'] + # note: taint of attribute lookups is handled in QL diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index 3c23b3929911..876691648e7d 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -338,7 +338,7 @@ module StdlibPrivate { * Modeling of path related functions in the `os` module. * Wrapped in QL module to make it easy to fold/unfold. */ - private module OsFileSystemAccessModeling { + module OsFileSystemAccessModeling { /** * A call to the `os.fsencode` function. * @@ -395,7 +395,7 @@ module StdlibPrivate { * * See https://docs.python.org/3/library/os.html#os.open */ - private class OsOpenCall extends FileSystemAccess::Range, DataFlow::CallCfgNode { + class OsOpenCall extends FileSystemAccess::Range, DataFlow::CallCfgNode { OsOpenCall() { this = os().getMember("open").getACall() } override DataFlow::Node getAPathArgument() { @@ -1499,13 +1499,22 @@ module StdlibPrivate { * See https://docs.python.org/3/library/functions.html#open */ private class OpenCall extends FileSystemAccess::Range, Stdlib::FileLikeObject::InstanceSource, - DataFlow::CallCfgNode + ThreatModelSource::Range, DataFlow::CallCfgNode { - OpenCall() { this = getOpenFunctionRef().getACall() } + OpenCall() { + this = getOpenFunctionRef().getACall() and + // when analyzing stdlib code for os.py we wrongly assume that `os.open` is an + // alias of the builtins `open` function + not this instanceof OsFileSystemAccessModeling::OsOpenCall + } override DataFlow::Node getAPathArgument() { result in [this.getArg(0), this.getArgByName("file")] } + + override string getThreatModel() { result = "file" } + + override string getSourceType() { result = "open()" } } /** @@ -4989,6 +4998,39 @@ module StdlibPrivate { override string getKind() { result = Escaping::getHtmlKind() } } + + // --------------------------------------------------------------------------- + // argparse + // --------------------------------------------------------------------------- + /** + * if result of `parse_args` is tainted (because it uses command-line arguments), + * then the parsed values accesssed on any attribute lookup is also tainted. + */ + private class ArgumentParserAnyAttributeStep extends TaintTracking::AdditionalTaintStep { + override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + nodeFrom = + API::moduleImport("argparse") + .getMember("ArgumentParser") + .getReturn() + .getMember("parse_args") + .getReturn() + .getAValueReachableFromSource() and + nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom + } + } + + // --------------------------------------------------------------------------- + // sys + // --------------------------------------------------------------------------- + /** + * An access of `sys.stdin`/`sys.stdout`/`sys.stderr`, to get additional FileLike + * modeling. + */ + private class SysStandardStreams extends Stdlib::FileLikeObject::InstanceSource, DataFlow::Node { + SysStandardStreams() { + this = API::moduleImport("sys").getMember(["stdin", "stdout", "stderr"]).asSource() + } + } } // --------------------------------------------------------------------------- diff --git a/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll index c2176c0644b9..11c6b285f2aa 100644 --- a/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll +++ b/python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll @@ -18,14 +18,19 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.DataFlow private import semmle.python.ApiGraphs private import semmle.python.dataflow.new.FlowSummary +private import semmle.python.Concepts /** - * A remote flow source originating from a CSV source row. + * A threat-model flow source originating from a data extension. */ -private class RemoteFlowSourceFromCsv extends RemoteFlowSource::Range { - RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").asSource() } +private class ThreatModelSourceFromDataExtension extends ThreatModelSource::Range { + ThreatModelSourceFromDataExtension() { this = ModelOutput::getASourceNode(_).asSource() } - override string getSourceType() { result = "Remote flow (from model)" } + override string getThreatModel() { this = ModelOutput::getASourceNode(result).asSource() } + + override string getSourceType() { + result = "Source node (" + this.getThreatModel() + ") [from data-extension]" + } } private class SummarizedCallableFromModel extends SummarizedCallable { diff --git a/python/ql/lib/semmle/python/security/dataflow/CodeInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/CodeInjectionCustomizations.qll index 294b3c63fdad..354d098186f6 100644 --- a/python/ql/lib/semmle/python/security/dataflow/CodeInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/CodeInjectionCustomizations.qll @@ -33,9 +33,14 @@ module CodeInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A code execution, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/CommandInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/CommandInjectionCustomizations.qll index a8d17026e40c..f3786700967b 100644 --- a/python/ql/lib/semmle/python/security/dataflow/CommandInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/CommandInjectionCustomizations.qll @@ -33,9 +33,14 @@ module CommandInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A command execution, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/CookieInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/CookieInjectionCustomizations.qll index dd3792182de8..cee2b7b98e73 100644 --- a/python/ql/lib/semmle/python/security/dataflow/CookieInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/CookieInjectionCustomizations.qll @@ -31,9 +31,14 @@ module CookieInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A write to a cookie, considered as a sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/HttpHeaderInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/HttpHeaderInjectionCustomizations.qll index e529d3f29e0f..92cd46a3408a 100644 --- a/python/ql/lib/semmle/python/security/dataflow/HttpHeaderInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/HttpHeaderInjectionCustomizations.qll @@ -32,9 +32,14 @@ module HttpHeaderInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A HTTP header write, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll index 6c2b664bd965..f0229bc0c824 100644 --- a/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll @@ -42,9 +42,14 @@ module LdapInjection { abstract class FilterSanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A logging operation, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/LogInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/LogInjectionCustomizations.qll index f92b87c73faf..71648b2c6281 100644 --- a/python/ql/lib/semmle/python/security/dataflow/LogInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/LogInjectionCustomizations.qll @@ -33,9 +33,14 @@ module LogInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A logging operation, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/PamAuthorizationCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/PamAuthorizationCustomizations.qll index afba208e0e45..eb858be8f95d 100644 --- a/python/ql/lib/semmle/python/security/dataflow/PamAuthorizationCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/PamAuthorizationCustomizations.qll @@ -7,6 +7,7 @@ import python import semmle.python.ApiGraphs import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.Concepts /** * Provides default sources, sinks and sanitizers for detecting @@ -39,9 +40,14 @@ module PamAuthorizationCustomizations { abstract class Sink extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A vulnerable `pam_authenticate` call considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll index cda71df2f905..eff7d715c268 100644 --- a/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll @@ -43,9 +43,14 @@ module PathInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A file system access, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll index 23cd45312300..2cd8b0a64bd7 100644 --- a/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/PolynomialReDoSCustomizations.qll @@ -47,9 +47,14 @@ module PolynomialReDoS { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A regex execution, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll index ee2dec407d14..3a7177eb4c74 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll @@ -33,9 +33,14 @@ module ReflectedXss { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A data flow sink for "reflected cross-site scripting" vulnerabilities. diff --git a/python/ql/lib/semmle/python/security/dataflow/RegexInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/RegexInjectionCustomizations.qll index 72dc66430b6d..559b1f66e7e6 100644 --- a/python/ql/lib/semmle/python/security/dataflow/RegexInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/RegexInjectionCustomizations.qll @@ -40,9 +40,14 @@ module RegexInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A regex escaping, considered as a sanitizer. diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index a4e3ecc9ee18..552d022c98ec 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -45,9 +45,14 @@ module ServerSideRequestForgery { abstract class FullUrlControlSanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** The URL of an HTTP request, considered as a sink. */ class HttpRequestUrlAsSink extends Sink { diff --git a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll index 7e0969d854f0..e2a5b4d624a2 100644 --- a/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll @@ -32,9 +32,14 @@ module SqlInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A SQL statement of a SQL construction, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/UnsafeDeserializationCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/UnsafeDeserializationCustomizations.qll index 27b6a44580d0..ca909e5de1a0 100644 --- a/python/ql/lib/semmle/python/security/dataflow/UnsafeDeserializationCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/UnsafeDeserializationCustomizations.qll @@ -33,9 +33,14 @@ module UnsafeDeserialization { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * An insecure decoding, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll index fa913b28fbdf..0c05a36eaebb 100644 --- a/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll @@ -77,9 +77,14 @@ module UrlRedirect { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A HTTP redirect response, considered as a flow sink. diff --git a/python/ql/lib/semmle/python/security/dataflow/XpathInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/XpathInjectionCustomizations.qll index ef30b3f81ce0..10b47f174390 100644 --- a/python/ql/lib/semmle/python/security/dataflow/XpathInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/XpathInjectionCustomizations.qll @@ -30,9 +30,14 @@ module XpathInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A construction of an XPath expression, considered as a sink. diff --git a/python/ql/src/experimental/Security/CWE-074/TemplateInjectionCustomizations.qll b/python/ql/src/experimental/Security/CWE-074/TemplateInjectionCustomizations.qll index dcd6c1848616..e5c5013f3ba6 100644 --- a/python/ql/src/experimental/Security/CWE-074/TemplateInjectionCustomizations.qll +++ b/python/ql/src/experimental/Security/CWE-074/TemplateInjectionCustomizations.qll @@ -33,9 +33,14 @@ module TemplateInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * A SQL statement of a SQL construction, considered as a flow sink. diff --git a/python/ql/src/experimental/Security/CWE-091/XsltInjectionCustomizations.qll b/python/ql/src/experimental/Security/CWE-091/XsltInjectionCustomizations.qll index bda2fe646c95..4cacf1f85dee 100644 --- a/python/ql/src/experimental/Security/CWE-091/XsltInjectionCustomizations.qll +++ b/python/ql/src/experimental/Security/CWE-091/XsltInjectionCustomizations.qll @@ -33,9 +33,14 @@ module XsltInjection { abstract class Sanitizer extends DataFlow::Node { } /** - * A source of remote user input, considered as a flow source. + * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead! */ - class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { } + deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource; + + /** + * An active threat-model source, considered as a flow source. + */ + private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { } /** * An XSLT construction, considered as a flow sink. diff --git a/python/ql/src/experimental/Security/CWE-094/Js2Py.ql b/python/ql/src/experimental/Security/CWE-094/Js2Py.ql index 5dc160077873..f5d6e3a6c10e 100644 --- a/python/ql/src/experimental/Security/CWE-094/Js2Py.ql +++ b/python/ql/src/experimental/Security/CWE-094/Js2Py.ql @@ -18,7 +18,7 @@ import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.Concepts module Js2PyFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } + predicate isSource(DataFlow::Node node) { node instanceof ActiveThreatModelSource } predicate isSink(DataFlow::Node node) { API::moduleImport("js2py").getMember(["eval_js", "eval_js6", "EvalJs"]).getACall().getArg(_) = diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index a53171de88ac..87addb8a1fc3 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -3,6 +3,7 @@ import semmle.python.dataflow.new.DataFlow import semmle.python.Concepts import TestUtilities.InlineExpectationsTest private import semmle.python.dataflow.new.internal.PrintNode +private import codeql.threatmodels.ThreatModels module SystemCommandExecutionTest implements TestSig { string getARelevantTag() { result = "getCommand" } @@ -632,6 +633,22 @@ module XmlParsingTest implements TestSig { } } +module ThreatModelSourceTest implements TestSig { + string getARelevantTag() { + exists(string kind | knownThreatModel(kind) | result = "threatModelSource" + "[" + kind + "]") + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(ThreatModelSource src | not src.getThreatModel() = "remote" | + location = src.getLocation() and + element = src.toString() and + value = prettyNodeForInlineTest(src) and + tag = "threatModelSource[" + src.getThreatModel() + "]" + ) + } +} + import MakeTest, MergeTests5, MergeTests5>> + CsrfLocalProtectionSettingTest, MergeTests>>> diff --git a/python/ql/test/experimental/meta/InlineTaintTest.qll b/python/ql/test/experimental/meta/InlineTaintTest.qll index 24f67bcf2a45..a09cc9aabc19 100644 --- a/python/ql/test/experimental/meta/InlineTaintTest.qll +++ b/python/ql/test/experimental/meta/InlineTaintTest.qll @@ -15,6 +15,7 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources import TestUtilities.InlineExpectationsTest private import semmle.python.dataflow.new.internal.PrintNode +private import semmle.python.Concepts DataFlow::Node shouldBeTainted() { exists(DataFlow::CallCfgNode call | @@ -45,7 +46,7 @@ module Conf { source.(DataFlow::CfgNode).getNode() = call.getAnArg() ) or - source instanceof RemoteFlowSource + source instanceof ThreatModelSource } predicate isSink(DataFlow::Node sink) { diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/manage.py b/python/ql/test/library-tests/frameworks/django-v2-v3/manage.py index 0e1a0b64a6e6..9fa5846c9551 100755 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/manage.py +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/manage.py @@ -6,7 +6,7 @@ def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') # $ threatModelSource[environment]=os.environ try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -15,7 +15,7 @@ def main(): "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc - execute_from_command_line(sys.argv) + execute_from_command_line(sys.argv) # $ threatModelSource[commandargs]=sys.argv if __name__ == '__main__': diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/asgi.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/asgi.py index 33b113ce911d..4906268e4161 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/asgi.py +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/asgi.py @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') # $ threatModelSource[environment]=os.environ application = get_asgi_application() diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/wsgi.py b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/wsgi.py index b466637895b7..a3f803658abc 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/wsgi.py +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/testproj/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') # $ threatModelSource[environment]=os.environ application = get_wsgi_application() diff --git a/python/ql/test/library-tests/frameworks/psycopg/pep249.py b/python/ql/test/library-tests/frameworks/psycopg/pep249.py index 0336facb079e..e61700d16ed6 100644 --- a/python/ql/test/library-tests/frameworks/psycopg/pep249.py +++ b/python/ql/test/library-tests/frameworks/psycopg/pep249.py @@ -12,3 +12,24 @@ with conn.cursor() as cursor: cursor.execute("some sql", (42,)) # $ getSql="some sql" cursor.executemany("some sql", [(42,)]) # $ getSql="some sql" + + + ### test of threat-model sources + row = cursor.fetchone() # $ threatModelSource[database]=cursor.fetchone() + rows_many = cursor.fetchmany(10) # $ threatModelSource[database]=cursor.fetchmany(..) + rows_all = cursor.fetchall() # $ threatModelSource[database]=cursor.fetchall() + + ensure_tainted( + row[0], # $ tainted + rows_many[0][0], # $ tainted + rows_all[0][0], # $ tainted + + # pretending we created cursor to return dictionary results + row["column"], # $ tainted + rows_many[0]["column"], # $ tainted + rows_all[0]["column"], # $ tainted + ) + for row in rows_many: + ensure_tainted(row[0], row["column"]) # $ tainted + for row in rows_all: + ensure_tainted(row[0], row["column"]) # tainted diff --git a/python/ql/test/library-tests/frameworks/rest_framework/manage.py b/python/ql/test/library-tests/frameworks/rest_framework/manage.py index 0e1a0b64a6e6..9fa5846c9551 100755 --- a/python/ql/test/library-tests/frameworks/rest_framework/manage.py +++ b/python/ql/test/library-tests/frameworks/rest_framework/manage.py @@ -6,7 +6,7 @@ def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproj.settings') # $ threatModelSource[environment]=os.environ try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -15,7 +15,7 @@ def main(): "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc - execute_from_command_line(sys.argv) + execute_from_command_line(sys.argv) # $ threatModelSource[commandargs]=sys.argv if __name__ == '__main__': diff --git a/python/ql/test/library-tests/frameworks/stdlib-py3/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/stdlib-py3/FileSystemAccess.py index 4de7f3a3c329..45eff39d82cb 100644 --- a/python/ql/test/library-tests/frameworks/stdlib-py3/FileSystemAccess.py +++ b/python/ql/test/library-tests/frameworks/stdlib-py3/FileSystemAccess.py @@ -17,7 +17,7 @@ name = windows.parent.name o = open -o(name) # $ getAPathArgument=name +o(name) # $ getAPathArgument=name threatModelSource[file]=o(..) wb = p.write_bytes wb(b"hello") # $ getAPathArgument=p fileWriteData=b"hello" diff --git a/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py b/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py index 197ccd8eb919..7d52f53858cf 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py +++ b/python/ql/test/library-tests/frameworks/stdlib/FileSystemAccess.py @@ -5,25 +5,25 @@ import tempfile import shutil -open("file") # $ getAPathArgument="file" -open(file="file") # $ getAPathArgument="file" +open("file") # $ getAPathArgument="file" threatModelSource[file]=open(..) +open(file="file") # $ getAPathArgument="file" threatModelSource[file]=open(..) o = open -o("file") # $ getAPathArgument="file" -o(file="file") # $ getAPathArgument="file" +o("file") # $ getAPathArgument="file" threatModelSource[file]=o(..) +o(file="file") # $ getAPathArgument="file" threatModelSource[file]=o(..) -builtins.open("file") # $ getAPathArgument="file" -builtins.open(file="file") # $ getAPathArgument="file" +builtins.open("file") # $ getAPathArgument="file" threatModelSource[file]=builtins.open(..) +builtins.open(file="file") # $ getAPathArgument="file" threatModelSource[file]=builtins.open(..) -io.open("file") # $ getAPathArgument="file" -io.open(file="file") # $ getAPathArgument="file" +io.open("file") # $ getAPathArgument="file" threatModelSource[file]=io.open(..) +io.open(file="file") # $ getAPathArgument="file" threatModelSource[file]=io.open(..) io.open_code("file") # $ getAPathArgument="file" io.FileIO("file") # $ getAPathArgument="file" -f = open("path") # $ getAPathArgument="path" +f = open("path") # $ getAPathArgument="path" threatModelSource[file]=open(..) f.write("foo") # $ getAPathArgument="path" fileWriteData="foo" lines = ["foo"] f.writelines(lines) # $ getAPathArgument="path" fileWriteData=lines diff --git a/python/ql/test/library-tests/frameworks/stdlib/threat_models.py b/python/ql/test/library-tests/frameworks/stdlib/threat_models.py new file mode 100644 index 000000000000..23b800ce576e --- /dev/null +++ b/python/ql/test/library-tests/frameworks/stdlib/threat_models.py @@ -0,0 +1,71 @@ +import os +import sys +import posix + +ensure_tainted( + os.getenv("foo"), # $ tainted threatModelSource[environment]=os.getenv(..) + os.getenvb("bar"), # $ tainted threatModelSource[environment]=os.getenvb(..) + + os.environ["foo"], # $ tainted threatModelSource[environment]=os.environ + os.environ.get("foo"), # $ tainted threatModelSource[environment]=os.environ + + os.environb["bar"], # $ tainted threatModelSource[environment]=os.environb + posix.environ[b"foo"], # $ tainted threatModelSource[environment]=posix.environ + + + sys.argv[1], # $ tainted threatModelSource[commandargs]=sys.argv + sys.orig_argv[1], # $ tainted threatModelSource[commandargs]=sys.orig_argv +) + +for k,v in os.environ.items(): # $ threatModelSource[environment]=os.environ + ensure_tainted(k) # $ tainted + ensure_tainted(v) # $ tainted + + +######################################## +# argparse +######################################## + +import argparse +parser = argparse.ArgumentParser() +parser.add_argument("foo") + +args = parser.parse_args() # $ threatModelSource[commandargs]=parser.parse_args() +ensure_tainted(args.foo) # $ tainted + +explicit_argv_parsing = parser.parse_args(sys.argv) # $ threatModelSource[commandargs]=sys.argv +ensure_tainted(explicit_argv_parsing.foo) # $ tainted + +fake_args = parser.parse_args([""]) +ensure_not_tainted(fake_args.foo) # $ SPURIOUS: tainted + +######################################## +# reading input from stdin +######################################## + +ensure_tainted( + sys.stdin.readline(), # $ tainted threatModelSource[stdin]=sys.stdin + input(), # $ tainted threatModelSource[stdin]=input() +) + +######################################## +# reading data from files +######################################## + +ensure_tainted( + open("foo"), # $ tainted threatModelSource[file]=open(..) getAPathArgument="foo" + open("foo").read(), # $ tainted threatModelSource[file]=open(..) getAPathArgument="foo" + open("foo").readline(), # $ tainted threatModelSource[file]=open(..) getAPathArgument="foo" + open("foo").readlines(), # $ tainted threatModelSource[file]=open(..) getAPathArgument="foo" + + os.read(os.open("foo"), 1024), # $ tainted threatModelSource[file]=os.read(..) getAPathArgument="foo" +) + +######################################## +# socket +######################################## + +import socket +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.connect(("example.com", 1234)) +ensure_tainted(s.recv(1024)) # $ MISSING: tainted threatModelSource[socket] diff --git a/python/ql/test/library-tests/frameworks/stdlib/wsgiref_simple_server_test.py b/python/ql/test/library-tests/frameworks/stdlib/wsgiref_simple_server_test.py index 7327385c0647..fd852337aba3 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/wsgiref_simple_server_test.py +++ b/python/ql/test/library-tests/frameworks/stdlib/wsgiref_simple_server_test.py @@ -45,7 +45,7 @@ def func2(environ, start_response): # $ requestHandler start_response(status, headers) # $ headerWriteBulk=headers headerWriteBulkUnsanitized=name,value return [b"Hello"] # $ HttpResponse responseBody=List -case = sys.argv[1] +case = sys.argv[1] # $ threatModelSource[commandargs]=sys.argv if case == "1": server = wsgiref.simple_server.WSGIServer(ADDRESS, wsgiref.simple_server.WSGIRequestHandler) server.set_app(func) diff --git a/python/ql/test/library-tests/threat-models/default/ActiveKinds.expected b/python/ql/test/library-tests/threat-models/default/ActiveKinds.expected new file mode 100644 index 000000000000..892f0fa5f6c3 --- /dev/null +++ b/python/ql/test/library-tests/threat-models/default/ActiveKinds.expected @@ -0,0 +1,3 @@ +| default | +| remote | +| request | diff --git a/python/ql/test/library-tests/threat-models/default/ActiveKinds.ql b/python/ql/test/library-tests/threat-models/default/ActiveKinds.ql new file mode 100644 index 000000000000..93a1354b7af8 --- /dev/null +++ b/python/ql/test/library-tests/threat-models/default/ActiveKinds.ql @@ -0,0 +1,7 @@ +private import codeql.threatmodels.ThreatModels + +from string kind +where + knownThreatModel(kind) and + currentThreatModel(kind) +select kind diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.expected b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.expected new file mode 100644 index 000000000000..1e4ba8b95305 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.expected @@ -0,0 +1,8 @@ +edges +| test.py:6:14:6:21 | ControlFlowNode for Attribute | test.py:6:14:6:24 | ControlFlowNode for Subscript | provenance | Src:MaD:17 | +nodes +| test.py:6:14:6:21 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:6:14:6:24 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +subpaths +#select +| test.py:6:14:6:24 | ControlFlowNode for Subscript | test.py:6:14:6:21 | ControlFlowNode for Attribute | test.py:6:14:6:24 | ControlFlowNode for Subscript | This SQL query depends on a $@. | test.py:6:14:6:21 | ControlFlowNode for Attribute | user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.ext.yml b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.ext.yml new file mode 100644 index 000000000000..63507f477386 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.ext.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/threat-models + extensible: threatModelConfiguration + data: + - ["local", true, 0] diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.qlref b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.qlref new file mode 100644 index 000000000000..d1d02cbe8d37 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/SqlInjection.qlref @@ -0,0 +1 @@ +Security/CWE-089/SqlInjection.ql diff --git a/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/test.py b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/test.py new file mode 100644 index 000000000000..97bfa393cedf --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-089-SqlInjection-local-threat-model/test.py @@ -0,0 +1,6 @@ +# test that enabling local threat-model works end-to-end +import sys +import psycopg + +conn = psycopg.connect(...) +conn.execute(sys.argv[1]) diff --git a/shared/threat-models/codeql/threatmodels/ThreatModels.qll b/shared/threat-models/codeql/threatmodels/ThreatModels.qll index d12139ef28ea..19dfd0d1a656 100644 --- a/shared/threat-models/codeql/threatmodels/ThreatModels.qll +++ b/shared/threat-models/codeql/threatmodels/ThreatModels.qll @@ -29,7 +29,7 @@ extensible predicate threatModelConfiguration(string kind, boolean enable, int p extensible private predicate threatModelGrouping(string kind, string group); /** Holds if the specified threat model kind is mentioned in either the configuration or grouping table. */ -private predicate knownThreatModel(string kind) { +predicate knownThreatModel(string kind) { threatModelConfiguration(kind, _, _) or threatModelGrouping(kind, _) or threatModelGrouping(_, kind) or