Merge remote-tracking branch 'upstream/main' into 'rc/3.14'

This commit is contained in:
Arthur Baars
2024-06-28 19:50:35 +02:00
772 changed files with 16846 additions and 17035 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* A number of Python queries now support sinks defined using data extensions. The format of data extensions for Python has been documented.

View File

@@ -35,6 +35,7 @@ private import semmle.python.frameworks.Idna
private import semmle.python.frameworks.Invoke
private import semmle.python.frameworks.Jmespath
private import semmle.python.frameworks.Joblib
private import semmle.python.frameworks.JsonPickle
private import semmle.python.frameworks.Ldap
private import semmle.python.frameworks.Ldap3
private import semmle.python.frameworks.Libtaxii
@@ -48,7 +49,9 @@ private import semmle.python.frameworks.Numpy
private import semmle.python.frameworks.Opml
private import semmle.python.frameworks.Oracledb
private import semmle.python.frameworks.Pandas
private import semmle.python.frameworks.Paramiko
private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Pexpect
private import semmle.python.frameworks.Phoenixdb
private import semmle.python.frameworks.Psycopg
private import semmle.python.frameworks.Psycopg2
@@ -71,6 +74,7 @@ private import semmle.python.frameworks.SqlAlchemy
private import semmle.python.frameworks.Starlette
private import semmle.python.frameworks.Stdlib
private import semmle.python.frameworks.Toml
private import semmle.python.frameworks.Torch
private import semmle.python.frameworks.Tornado
private import semmle.python.frameworks.Twisted
private import semmle.python.frameworks.Ujson

View File

@@ -564,7 +564,6 @@ predicate neverSkipInPathGraph(Node n) {
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
*/
pragma[inline]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() }
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
@@ -576,8 +575,7 @@ predicate localMustFlowStep(Node nodeFrom, Node nodeTo) { none() }
*/
DataFlowType getNodeType(Node node) {
result = TAnyFlow() and
// Suppress unused variable warning
node = node
exists(node)
}
/** Gets a string representation of a type returned by `getErasedRepr`. */

View File

@@ -29,8 +29,8 @@ private module FabricV1 {
// -------------------------------------------------------------------------
// fabric.api
// -------------------------------------------------------------------------
/** Gets a reference to the `fabric.api` module. */
API::Node api() { result = fabric().getMember("api") }
/** Gets a reference to the `fabric.api` module. Also known as `fabric.operations` */
API::Node api() { result = fabric().getMember(["api", "operations"]) }
/** Provides models for the `fabric.api` module */
module Api {
@@ -71,7 +71,7 @@ private module FabricV1 {
* Provides classes modeling security-relevant aspects of the `fabric` PyPI package, for
* version 2.x.
*
* See http://docs.fabfile.org/en/2.5/getting-st arted.html.
* See http://docs.fabfile.org/en/2.5/getting-started.html.
*/
module FabricV2 {
/** Gets a reference to the `fabric` module. */
@@ -93,7 +93,9 @@ module FabricV2 {
* See https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.
*/
module ConnectionClass {
/** Gets a reference to the `fabric.connection.Connection` class. */
/**
* Gets a reference to the `fabric.connection.Connection` class.
*/
API::Node classRef() {
result = fabric().getMember("Connection")
or
@@ -109,40 +111,16 @@ module FabricV2 {
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `Connection::instance()` to get references to instances of `fabric.connection.Connection`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
abstract class Instance extends API::Node {
override string toString() { result = this.(API::Node).toString() }
}
/** Gets a reference to an instance of `fabric.connection.Connection`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `fabric.connection.Connection`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* Gets a reference to either `run`, `sudo`, or `local` method on a
* `fabric.connection.Connection` instance.
*
* See
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.run
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.sudo
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.local
* A reference to the `fabric.connection.Connection` class.
*/
private DataFlow::TypeTrackingNode instanceRunMethods(DataFlow::TypeTracker t) {
t.startInAttr(["run", "sudo", "local"]) and
result = instance()
or
exists(DataFlow::TypeTracker t2 | result = instanceRunMethods(t2).track(t2, t))
class ClassInstantiation extends Instance {
ClassInstantiation() { this = classRef().getReturn() }
}
/**
@@ -154,8 +132,8 @@ module FabricV2 {
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.sudo
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.local
*/
DataFlow::Node instanceRunMethods() {
instanceRunMethods(DataFlow::TypeTracker::end()).flowsTo(result)
API::CallNode instanceRunMethods() {
result = any(Instance is).getMember(["run", "sudo", "local"]).getACall()
}
}
}
@@ -168,19 +146,38 @@ module FabricV2 {
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.local
*/
private class FabricConnectionRunSudoLocalCall extends SystemCommandExecution::Range,
DataFlow::CallCfgNode
API::CallNode
{
FabricConnectionRunSudoLocalCall() {
this.getFunction() = Fabric::Connection::ConnectionClass::instanceRunMethods()
this = Fabric::Connection::ConnectionClass::instanceRunMethods()
}
override DataFlow::Node getCommand() {
result = [this.getArg(0), this.getArgByName("command")]
}
override DataFlow::Node getCommand() { result = this.getParameter(0, "command").asSink() }
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
}
/**
* A `gateway` parameter of `fabric.connection.Connection` instance is considered as ssh proxy_command option and can execute command.
* See https://docs.fabfile.org/en/latest/api/connection.html#fabric.connection.Connection
*/
private class FabricConnectionProxyCommand extends SystemCommandExecution::Range, API::CallNode {
FabricConnectionProxyCommand() {
this = Fabric::Connection::ConnectionClass::classRef().getACall() and
// we want to make sure that the connection is established otherwise the command of proxy_command won't run.
exists(
this.getAMethodCall([
"run", "get", "sudo", "open_gateway", "open", "create_session", "forward_local",
"forward_remote", "put", "shell", "sftp"
])
)
}
override DataFlow::Node getCommand() { result = this.getParameter(4, "gateway").asSink() }
override predicate isShellInterpreted(DataFlow::Node arg) { none() }
}
// -------------------------------------------------------------------------
// fabric.tasks
// -------------------------------------------------------------------------
@@ -193,14 +190,10 @@ module FabricV2 {
API::Node task() { result in [tasks().getMember("task"), fabric().getMember("task")] }
}
class FabricTaskFirstParamConnectionInstance extends Fabric::Connection::ConnectionClass::InstanceSource,
DataFlow::ParameterNode
class FabricTaskFirstParamConnectionInstance extends Fabric::Connection::ConnectionClass::Instance
{
FabricTaskFirstParamConnectionInstance() {
exists(Function func |
func.getADecorator() = Fabric::Tasks::task().getAValueReachableFromSource().asExpr() and
this.getParameter() = func.getArg(0)
)
this = Fabric::Tasks::task().getParameter(0).getParameter(0)
}
}
@@ -242,11 +235,14 @@ module FabricV2 {
API::Node subclassInstance() { result = any(ModeledSubclass m).getASubclass*().getReturn() }
/**
* Gets a reference to the `run` method on an instance of a subclass of `fabric.group.Group`.
* Gets a reference to the `run` and `sudo` methods on an instance of a subclass of `fabric.group.Group`.
*
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
* See https://docs.fabfile.org/en/latest/api/group.html#fabric.group.Group.sudo
*/
API::Node subclassInstanceRunMethod() { result = subclassInstance().getMember("run") }
API::Node subclassInstanceRunMethod() {
result = subclassInstance().getMember(["run", "sudo"])
}
}
/**
@@ -254,14 +250,12 @@ module FabricV2 {
*
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
*/
private class FabricGroupRunCall extends SystemCommandExecution::Range, DataFlow::CallCfgNode {
private class FabricGroupRunCall extends SystemCommandExecution::Range, API::CallNode {
FabricGroupRunCall() {
this = Fabric::Group::GroupClass::subclassInstanceRunMethod().getACall()
}
override DataFlow::Node getCommand() {
result = [this.getArg(0), this.getArgByName("command")]
}
override DataFlow::Node getCommand() { result = this.getParameter(0, "command").asSink() }
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
}

View File

@@ -0,0 +1,31 @@
/**
* Provides classes modeling security-relevant aspects of the `jsonpickle` PyPI package.
* See https://pypi.org/project/jsonpickle/.
*/
private import python
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides models for the `jsonpickle` PyPI package.
* See https://pypi.org/project/jsonpickle/.
*/
private module Jsonpickle {
/**
* A Call to `jsonpickle.decode`.
* See https://jsonpickle.readthedocs.io/en/latest/api.html#jsonpickle.decode
*/
private class JsonpickleDecode extends Decoding::Range, API::CallNode {
JsonpickleDecode() { this = API::moduleImport("jsonpickle").getMember("decode").getACall() }
override predicate mayExecuteInput() { any() }
override DataFlow::Node getAnInput() { result = this.getParameter(0, "string").asSink() }
override DataFlow::Node getOutput() { result = this }
override string getFormat() { result = "pickle" }
}
}

View File

@@ -34,4 +34,121 @@ private module Pandas {
override string getFormat() { result = "pickle" }
}
/**
* Provides security related models for `pandas.DataFrame`.
* See https://pandas.pydata.org/docs/reference/frame.html
*/
module DataFrame {
/**
* A `pandas.DataFrame` Object.
*
* Extend this class to model new APIs.
* See https://pandas.pydata.org/docs/reference/frame.html
*/
abstract class DataFrame extends API::Node {
override string toString() { result = this.(API::Node).toString() }
}
/**
* A `pandas.DataFrame` instantiation.
* See https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html
*/
class DataFrameConstructor extends DataFrame {
DataFrameConstructor() {
this = API::moduleImport("pandas").getMember("DataFrame").getReturn()
}
}
/**
* The `pandas.read_*` functions that return a `pandas.DataFrame`.
* See https://pandas.pydata.org/docs/reference/io.html
*/
class InputRead extends DataFrame {
InputRead() {
this =
API::moduleImport("pandas")
.getMember([
"read_csv", "read_fwf", "read_pickle", "read_table", "read_clipboard",
"read_excel", "read_xml", "read_parquet", "read_orc", "read_spss",
"read_sql_table", "read_sql_query", "read_sql", "read_gbq", "read_stata"
])
.getReturn()
or
this = API::moduleImport("pandas").getMember("read_html").getReturn().getASubscript()
or
exists(API::Node readSas, API::CallNode readSasCall |
readSas = API::moduleImport("pandas").getMember("read_sas") and
this = readSas.getReturn() and
readSasCall = readSas.getACall()
|
// Returns DataFrame if iterator=False and chunksize=None, Also with default values it returns DataFrame.
(
not readSasCall.getParameter(5, "iterator").asSink().asExpr().(BooleanLiteral)
instanceof True
or
not exists(readSasCall.getParameter(5, "iterator").asSink())
) and
not exists(
readSasCall.getParameter(4, "chunksize").asSink().asExpr().(IntegerLiteral).getN()
)
)
}
}
/**
* The `pandas.DataFrame.*` methods that return a `pandas.DataFrame` object.
* See https://pandas.pydata.org/docs/reference/io.html
*/
class DataFrameMethods extends DataFrame {
DataFrameMethods() {
this =
any(DataFrame df)
.getMember([
"copy", "from_records", "from_dict", "from_spmatrix", "assign", "select_dtypes",
"set_flags", "astype", "infer_objects", "head", "xs", "get", "isin", "where",
"mask", "query", "add", "mul", "truediv", "mod", "pow", "dot", "radd", "rsub",
"rdiv", "rfloordiv", "rtruediv", "rpow", "lt", "gt", "le", "ne", "agg", "combine",
"apply", "aggregate", "transform", "all", "any", "clip", "corr", "cov", "cummax",
"cummin", "cumprod", "describe", "mode", "pct_change", "quantile", "rank",
"round", "sem", "add_prefix", "add_suffix", "at_time", "between_time", "drop",
"drop_duplicates", "filter", "first", "head", "idxmin", "last", "reindex",
"reindex_like", "reset_index", "sample", "set_axis", "tail", "take", "truncate",
"bfill", "dropna", "ffill", "fillna", "interpolate", "isna", "isnull", "notna",
"notnull", "pad", "replace", "droplevel", "pivot", "pivot_table",
"reorder_levels", "sort_values", "sort_index", "nlargest", "nsmallest",
"swaplevel", "stack", "unstack", "isnull", "notna", "notnull", "replace",
"droplevel", "pivot", "pivot_table", "reorder_levels", "sort_values",
"sort_index", "nlargest", "nsmallest", "swaplevel", "stack", "unstack", "melt",
"explode", "squeeze", "T", "transpose", "compare", "join", "from_spmatrix",
"shift", "asof", "merge", "from_dict", "tz_convert", "to_period", "asfreq",
"to_dense", "tz_localize", "box", "__dataframe__"
])
.getReturn()
}
}
}
/**
* A Call to `pandas.DataFrame.query` or `pandas.DataFrame.eval`.
* See https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html
* https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.eval.html
*/
class CodeExecutionCall extends CodeExecution::Range, API::CallNode {
CodeExecutionCall() {
this = any(DataFrame::DataFrame df).getMember(["query", "eval"]).getACall()
}
override DataFlow::Node getCode() { result = this.getParameter(0, "expr").asSink() }
}
/**
* A Call to `pandas.eval`.
* See https://pandas.pydata.org/docs/reference/api/pandas.eval.html
*/
class PandasEval extends CodeExecution::Range, API::CallNode {
PandasEval() { this = API::moduleImport("pandas").getMember("eval").getACall() }
override DataFlow::Node getCode() { result = this.getParameter(0, "expr").asSink() }
}
}

View File

@@ -0,0 +1,32 @@
/**
* Provides classes modeling security-relevant aspects of the `paramiko` PyPI package.
* See https://pypi.org/project/paramiko/.
*/
private import python
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides models for the `paramiko` PyPI package.
* See https://pypi.org/project/paramiko/.
*/
private module Paramiko {
/**
* The first argument of `paramiko.ProxyCommand`.
*
* the `paramiko.ProxyCommand` is equivalent of `ssh -o ProxyCommand="CMD"`
* which runs `CMD` on the local system.
* See https://paramiko.pydata.org/docs/reference/api/paramiko.eval.html
*/
class ParamikoProxyCommand extends SystemCommandExecution::Range, API::CallNode {
ParamikoProxyCommand() {
this = API::moduleImport("paramiko").getMember("ProxyCommand").getACall()
}
override DataFlow::Node getCommand() { result = this.getParameter(0, "command_line").asSink() }
override predicate isShellInterpreted(DataFlow::Node arg) { none() }
}
}

View File

@@ -0,0 +1,45 @@
/**
* Provides classes modeling security-relevant aspects of the `pexpect` PyPI package.
* See https://pypi.org/project/pexpect/.
*/
private import python
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides models for the `pexpect` PyPI package.
* See https://pypi.org/project/pexpect/.
*/
private module Pexpect {
/**
* The calls to `pexpect.*` functions that execute commands
* See https://pexpect.readthedocs.io/en/stable/api/pexpect.html#pexpect.spawn
* See https://pexpect.readthedocs.io/en/stable/api/pexpect.html#pexpect.run
*/
class PexpectCommandExec extends SystemCommandExecution::Range, API::CallNode {
PexpectCommandExec() {
this = API::moduleImport("pexpect").getMember(["run", "runu", "spawn", "spawnu"]).getACall()
}
override DataFlow::Node getCommand() { result = this.getParameter(0, "command").asSink() }
override predicate isShellInterpreted(DataFlow::Node arg) { none() }
}
/**
* A call to `pexpect.popen_spawn.PopenSpawn`
* See https://pexpect.readthedocs.io/en/stable/api/popen_spawn.html#pexpect.popen_spawn.PopenSpawn
*/
class PexpectPopenSpawn extends SystemCommandExecution::Range, API::CallNode {
PexpectPopenSpawn() {
this =
API::moduleImport("pexpect").getMember("popen_spawn").getMember("PopenSpawn").getACall()
}
override DataFlow::Node getCommand() { result = this.getParameter(0, "cmd").asSink() }
override predicate isShellInterpreted(DataFlow::Node arg) { none() }
}
}

View File

@@ -0,0 +1,72 @@
/**
* Provides classes modeling security-relevant aspects of the `torch` PyPI package.
* See https://pypi.org/project/torch/.
*/
private import python
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides models for the `torch` PyPI package.
* See https://pypi.org/project/torch/.
*/
private module Torch {
/**
* A call to `torch.load`
* See https://pytorch.org/docs/stable/generated/torch.load.html#torch.load
*/
private class TorchLoadCall extends Decoding::Range, API::CallNode {
TorchLoadCall() { this = API::moduleImport("torch").getMember("load").getACall() }
override predicate mayExecuteInput() {
not exists(this.getParameter(2, "pickle_module").asSink()) or
this.getParameter(2, "pickle_module").asSink().asExpr() instanceof None
}
override DataFlow::Node getAnInput() { result = this.getParameter(0, "f").asSink() }
override DataFlow::Node getOutput() { result = this }
override string getFormat() { result = "pickle" }
}
/**
* A call to `torch.package.PackageImporter`
* See https://pytorch.org/docs/stable/package.html#torch.package.PackageImporter
*/
private class TorchPackageImporter extends Decoding::Range, API::CallNode {
TorchPackageImporter() {
this = API::moduleImport("torch").getMember("package").getMember("PackageImporter").getACall() and
exists(this.getAMethodCall("load_pickle"))
}
override predicate mayExecuteInput() { any() }
override DataFlow::Node getAnInput() {
result = this.getParameter(0, "file_or_buffer").asSink()
}
override DataFlow::Node getOutput() { result = this.getAMethodCall("load_pickle") }
override string getFormat() { result = "pickle" }
}
/**
* A call to `torch.jit.load`
* See https://pytorch.org/docs/stable/generated/torch.jit.load.html#torch.jit.load
*/
private class TorchJitLoad extends Decoding::Range, API::CallNode {
TorchJitLoad() {
this = API::moduleImport("torch").getMember("jit").getMember("load").getACall()
}
override predicate mayExecuteInput() { any() }
override DataFlow::Node getAnInput() { result = this.getParameter(0, "f").asSink() }
override DataFlow::Node getOutput() { result = this }
override string getFormat() { result = "pickle" }
}
}

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.frameworks.data.ModelsAsData
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -43,6 +44,10 @@ module CodeInjection {
CodeExecutionAsSink() { this = any(CodeExecution e).getCode() }
}
private class SinkFromModel extends Sink {
SinkFromModel() { this = ModelOutput::getASinkNode("code-injection").asSink() }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.frameworks.data.ModelsAsData
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -78,6 +79,10 @@ module CommandInjection {
}
}
private class SinkFromModel extends Sink {
SinkFromModel() { this = ModelOutput::getASinkNode("command-injection").asSink() }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.frameworks.data.ModelsAsData
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -71,6 +72,10 @@ module LogInjection {
}
}
private class SinkFromModel extends Sink {
SinkFromModel() { this = ModelOutput::getASinkNode("log-injection").asSink() }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.ApiGraphs
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -137,4 +138,34 @@ module ServerSideRequestForgery {
)
}
}
/** A validation that a string does not contain certain characters, considered as a sanitizer. */
private class StringRestrictionSanitizerGuard extends Sanitizer {
StringRestrictionSanitizerGuard() {
this = DataFlow::BarrierGuard<stringRestriction/3>::getABarrierNode()
}
}
private predicate stringRestriction(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
exists(DataFlow::MethodCallNode call, DataFlow::Node strNode |
call.asCfgNode() = g and strNode.asCfgNode() = node
|
branch = true and
call.calls(strNode,
["isalnum", "isalpha", "isdecimal", "isdigit", "isidentifier", "isnumeric", "isspace"])
or
branch = true and
call = API::moduleImport("re").getMember(["match", "fullmatch"]).getACall() and
strNode = [call.getArg(1), call.getArgByName("string")]
or
branch = true and
call =
API::moduleImport("re")
.getMember("compile")
.getReturn()
.getMember(["match", "fullmatch"])
.getACall() and
strNode = [call.getArg(0), call.getArgByName("string")]
)
}
}

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.frameworks.data.ModelsAsData
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -48,6 +49,10 @@ module UnsafeDeserialization {
}
}
private class SinkFromModel extends Sink {
SinkFromModel() { this = ModelOutput::getASinkNode("unsafe-deserialization").asSink() }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.frameworks.data.ModelsAsData
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -89,6 +90,10 @@ module UrlRedirect {
}
}
private class SinkFromModel extends Sink {
SinkFromModel() { this = ModelOutput::getASinkNode("url-redirection").asSink() }
}
/**
* The right side of a string-concat, considered as a sanitizer.
*/