mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge branch 'main' into py-restframework
This commit is contained in:
@@ -1,3 +1,16 @@
|
||||
## 0.11.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added basic flow for attributes defined on classes, when the attribute lookup is on a direct reference to that class (so not instance, cls parameter, or self parameter). Example: class definition `class Foo: my_tuples = (dangerous, safe)` and usage `SINK(Foo.my_tuples[0])`.
|
||||
|
||||
## 0.11.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added support for functions decorated with `contextlib.contextmanager`.
|
||||
* Namespace packages in the form of regular packages with missing `__init__.py`-files are now allowed. This enables the analysis to resolve modules and functions inside such packages.
|
||||
|
||||
## 0.11.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added support for functions decorated with `contextlib.contextmanager`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
|
||||
- Added support for type parameters in function and class definitions, as well as the new Python 3.12 type alias statement.
|
||||
6
python/ql/lib/change-notes/released/0.11.2.md
Normal file
6
python/ql/lib/change-notes/released/0.11.2.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## 0.11.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added support for functions decorated with `contextlib.contextmanager`.
|
||||
* Namespace packages in the form of regular packages with missing `__init__.py`-files are now allowed. This enables the analysis to resolve modules and functions inside such packages.
|
||||
5
python/ql/lib/change-notes/released/0.11.3.md
Normal file
5
python/ql/lib/change-notes/released/0.11.3.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.11.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added basic flow for attributes defined on classes, when the attribute lookup is on a direct reference to that class (so not instance, cls parameter, or self parameter). Example: class definition `class Foo: my_tuples = (dangerous, safe)` and usage `SINK(Foo.my_tuples[0])`.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.11.1
|
||||
lastReleaseVersion: 0.11.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.11.2-dev
|
||||
version: 0.11.4-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -220,3 +220,56 @@ class StringList extends StringList_ { }
|
||||
|
||||
/** A list of aliases in an import statement */
|
||||
class AliasList extends AliasList_ { }
|
||||
|
||||
/** A generic type parameter, as seen in function, class, and type alias definitions. */
|
||||
class TypeParameter extends TypeParameter_, AstNode {
|
||||
/** Gets a textual representation of this element */
|
||||
override string toString() { result = TypeParameter_.super.toString() }
|
||||
|
||||
override AstNode getAChildNode() { none() }
|
||||
|
||||
override Scope getScope() {
|
||||
exists(Function f | this = f.getATypeParameter() and result = f)
|
||||
or
|
||||
exists(ClassExpr c | this = c.getATypeParameter() and result = c.getInnerScope())
|
||||
or
|
||||
// For `TypeAlias`, this is not quite right. Instead, `TypeAlias`es should define their own scopes, cf. https://docs.python.org/3.12/reference/executionmodel.html#annotation-scopes
|
||||
exists(TypeAlias t | this = t.getATypeParameter() and result = t.getScope())
|
||||
}
|
||||
|
||||
/** Gets the location of this element */
|
||||
override Location getLocation() { result = TypeParameter_.super.getLocation() }
|
||||
}
|
||||
|
||||
/** A list of type parameters */
|
||||
class TypeParameterList extends TypeParameterList_ { }
|
||||
|
||||
/** A parent of a `TypeParameterList`. Internal implementation class. */
|
||||
class TypeParameterListParent extends TypeParameterListParent_ { }
|
||||
|
||||
/** A type alias statement, such as `type T[T1,T2] = T3`. */
|
||||
class TypeAlias extends TypeAlias_, Stmt {
|
||||
/** Gets the name of this type alias. */
|
||||
override Name getName() { result = super.getName() }
|
||||
}
|
||||
|
||||
/** A type variable, with an optional bound, such as `T1` and `T2` in `type T[T1, T2: T3] = T4`. */
|
||||
class TypeVar extends TypeVar_, TypeParameter {
|
||||
override Name getName() { result = super.getName() }
|
||||
|
||||
override Expr getAChildNode() { result in [this.getName(), this.getBound()] }
|
||||
}
|
||||
|
||||
/** A type var tuple parameter, such as `*T1` in `type T[*T1] = T2`. */
|
||||
class TypeVarTuple extends TypeVarTuple_, TypeParameter {
|
||||
override Name getName() { result = super.getName() }
|
||||
|
||||
override Expr getAChildNode() { result = this.getName() }
|
||||
}
|
||||
|
||||
/** A param spec parameter, such as `**T1` in `type T[**T1] = T2`. */
|
||||
class ParamSpec extends ParamSpec_, TypeParameter {
|
||||
override Name getName() { result = super.getName() }
|
||||
|
||||
override Expr getAChildNode() { result = this.getName() }
|
||||
}
|
||||
|
||||
@@ -285,6 +285,15 @@ class ClassExpr_ extends @py_ClassExpr, Expr {
|
||||
/** Gets the class scope of this class definition. */
|
||||
Class getInnerScope() { py_Classes(result, this) }
|
||||
|
||||
/** Gets the type parameters of this class definition. */
|
||||
TypeParameterList getTypeParameters() { py_type_parameter_lists(result, this) }
|
||||
|
||||
/** Gets the nth type parameter of this class definition. */
|
||||
TypeParameter getTypeParameter(int index) { result = this.getTypeParameters().getItem(index) }
|
||||
|
||||
/** Gets a type parameter of this class definition. */
|
||||
TypeParameter getATypeParameter() { result = this.getTypeParameters().getAnItem() }
|
||||
|
||||
override string toString() { result = "ClassExpr" }
|
||||
}
|
||||
|
||||
@@ -554,6 +563,15 @@ class Function_ extends @py_Function {
|
||||
/** Whether the async property of this function is true. */
|
||||
predicate isAsync() { py_bools(this, 6) }
|
||||
|
||||
/** Gets the type parameters of this function. */
|
||||
TypeParameterList getTypeParameters() { py_type_parameter_lists(result, this) }
|
||||
|
||||
/** Gets the nth type parameter of this function. */
|
||||
TypeParameter getTypeParameter(int index) { result = this.getTypeParameters().getItem(index) }
|
||||
|
||||
/** Gets a type parameter of this function. */
|
||||
TypeParameter getATypeParameter() { result = this.getTypeParameters().getAnItem() }
|
||||
|
||||
/** Gets a parent of this function */
|
||||
FunctionParent getParent() { py_Functions(this, result) }
|
||||
|
||||
@@ -1103,6 +1121,14 @@ class Param_ extends @py_Param, ExprContext {
|
||||
override string toString() { result = "Param" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `ParamSpec` for further information. */
|
||||
class ParamSpec_ extends @py_ParamSpec, TypeParameter {
|
||||
/** Gets the name of this parameter spec. */
|
||||
Expr getName() { py_exprs(result, _, this, 1) }
|
||||
|
||||
override string toString() { result = "ParamSpec" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `Pass` for further information. */
|
||||
class Pass_ extends @py_Pass, Stmt {
|
||||
override string toString() { result = "Pass" }
|
||||
@@ -1412,6 +1438,45 @@ class Tuple_ extends @py_Tuple, Expr {
|
||||
override string toString() { result = "Tuple" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `TypeAlias` for further information. */
|
||||
class TypeAlias_ extends @py_TypeAlias, Stmt {
|
||||
/** Gets the name of this type alias. */
|
||||
Expr getName() { py_exprs(result, _, this, 1) }
|
||||
|
||||
/** Gets the type_parameters of this type alias. */
|
||||
TypeParameterList getTypeParameters() { py_type_parameter_lists(result, this) }
|
||||
|
||||
/** Gets the nth type_parameter of this type alias. */
|
||||
TypeParameter getTypeParameter(int index) { result = this.getTypeParameters().getItem(index) }
|
||||
|
||||
/** Gets a type_parameter of this type alias. */
|
||||
TypeParameter getATypeParameter() { result = this.getTypeParameters().getAnItem() }
|
||||
|
||||
/** Gets the value of this type alias. */
|
||||
Expr getValue() { py_exprs(result, _, this, 3) }
|
||||
|
||||
override string toString() { result = "TypeAlias" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `TypeVar` for further information. */
|
||||
class TypeVar_ extends @py_TypeVar, TypeParameter {
|
||||
/** Gets the name of this type variable. */
|
||||
Expr getName() { py_exprs(result, _, this, 1) }
|
||||
|
||||
/** Gets the bound of this type variable. */
|
||||
Expr getBound() { py_exprs(result, _, this, 2) }
|
||||
|
||||
override string toString() { result = "TypeVar" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `TypeVarTuple` for further information. */
|
||||
class TypeVarTuple_ extends @py_TypeVarTuple, TypeParameter {
|
||||
/** Gets the name of this type variable tuple. */
|
||||
Expr getName() { py_exprs(result, _, this, 1) }
|
||||
|
||||
override string toString() { result = "TypeVarTuple" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `UAdd` for further information. */
|
||||
class UAdd_ extends @py_UAdd, Unaryop {
|
||||
override string toString() { result = "UAdd" }
|
||||
@@ -1908,6 +1973,39 @@ class StrParent_ extends @py_str_parent {
|
||||
string toString() { result = "StrParent" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `TypeParameter` for further information. */
|
||||
class TypeParameter_ extends @py_type_parameter {
|
||||
/** Gets the location of this type parameter. */
|
||||
Location getLocation() { py_locations(result, this) }
|
||||
|
||||
/** Gets a parent of this type parameter */
|
||||
TypeParameterList getParent() { py_type_parameters(this, _, result, _) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TypeParameter" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `TypeParameterList` for further information. */
|
||||
class TypeParameterList_ extends @py_type_parameter_list {
|
||||
/** Gets a parent of this type parameter list */
|
||||
TypeParameterListParent getParent() { py_type_parameter_lists(this, result) }
|
||||
|
||||
/** Gets an item of this type parameter list */
|
||||
TypeParameter getAnItem() { py_type_parameters(result, _, this, _) }
|
||||
|
||||
/** Gets the nth item of this type parameter list */
|
||||
TypeParameter getItem(int index) { py_type_parameters(result, _, this, index) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TypeParameterList" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `TypeParameterListParent` for further information. */
|
||||
class TypeParameterListParent_ extends @py_type_parameter_list_parent {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TypeParameterListParent" }
|
||||
}
|
||||
|
||||
/** INTERNAL: See the class `Unaryop` for further information. */
|
||||
class Unaryop_ extends @py_unaryop {
|
||||
/** Gets a parent of this unary operation */
|
||||
|
||||
@@ -5,13 +5,18 @@
|
||||
// If you add modeling of a new framework/library, remember to add it to the docs in
|
||||
// `docs/codeql/reusables/supported-frameworks.rst`
|
||||
private import semmle.python.frameworks.Aioch
|
||||
private import semmle.python.frameworks.Aiofile
|
||||
private import semmle.python.frameworks.Aiofiles
|
||||
private import semmle.python.frameworks.Aiohttp
|
||||
private import semmle.python.frameworks.Aiomysql
|
||||
private import semmle.python.frameworks.Aiopg
|
||||
private import semmle.python.frameworks.Aiosqlite
|
||||
private import semmle.python.frameworks.Anyio
|
||||
private import semmle.python.frameworks.Asyncpg
|
||||
private import semmle.python.frameworks.Baize
|
||||
private import semmle.python.frameworks.BSon
|
||||
private import semmle.python.frameworks.CassandraDriver
|
||||
private import semmle.python.frameworks.Cherrypy
|
||||
private import semmle.python.frameworks.ClickhouseDriver
|
||||
private import semmle.python.frameworks.Cryptodome
|
||||
private import semmle.python.frameworks.Cryptography
|
||||
@@ -54,6 +59,7 @@ private import semmle.python.frameworks.Requests
|
||||
private import semmle.python.frameworks.RestFramework
|
||||
private import semmle.python.frameworks.Rsa
|
||||
private import semmle.python.frameworks.RuamelYaml
|
||||
private import semmle.python.frameworks.Sanic
|
||||
private import semmle.python.frameworks.ServerLess
|
||||
private import semmle.python.frameworks.Setuptools
|
||||
private import semmle.python.frameworks.Simplejson
|
||||
|
||||
@@ -179,21 +179,6 @@ private predicate legalDottedName(string name) {
|
||||
bindingset[name]
|
||||
private predicate legalShortName(string name) { name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*") }
|
||||
|
||||
/**
|
||||
* Holds if `f` is potentially a source package.
|
||||
* Does it have an __init__.py file (or --respect-init=False for Python 2) and is it within the source archive?
|
||||
*/
|
||||
private predicate isPotentialSourcePackage(Folder f) {
|
||||
f.getRelativePath() != "" and
|
||||
isPotentialPackage(f)
|
||||
}
|
||||
|
||||
private predicate isPotentialPackage(Folder f) {
|
||||
exists(f.getFile("__init__.py"))
|
||||
or
|
||||
py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 and exists(f)
|
||||
}
|
||||
|
||||
private string moduleNameFromBase(Container file) {
|
||||
// We used to also require `isPotentialPackage(f)` to hold in this case,
|
||||
// but we saw modules not getting resolved because their folder did not
|
||||
@@ -236,31 +221,114 @@ private predicate transitively_imported_from_entry_point(File file) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the folder `f` is a regular Python package,
|
||||
* containing an `__init__.py` file.
|
||||
*/
|
||||
private predicate isRegularPackage(Folder f, string name) {
|
||||
legalShortName(name) and
|
||||
name = f.getStem() and
|
||||
exists(f.getFile("__init__.py"))
|
||||
}
|
||||
|
||||
/** Gets the name of a module imported in package `c`. */
|
||||
private string moduleImportedInPackage(Container c) {
|
||||
legalShortName(result) and
|
||||
// it has to be imported in this folder
|
||||
result =
|
||||
any(ImportExpr i | i.getLocation().getFile().getParent() = c)
|
||||
.getName()
|
||||
// strip everything after the first `.`
|
||||
.regexpReplaceAll("\\..*", "") and
|
||||
result != ""
|
||||
}
|
||||
|
||||
/** Holds if the file `f` could be resolved to a module named `name`. */
|
||||
private predicate isPotentialModuleFile(File file, string name) {
|
||||
legalShortName(name) and
|
||||
name = file.getStem() and
|
||||
file.getExtension() = ["py", "pyc", "so", "pyd"] and
|
||||
// it has to be imported in this folder
|
||||
name = moduleImportedInPackage(file.getParent())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the folder `f` is a namespace package named `name`.
|
||||
*
|
||||
* See https://peps.python.org/pep-0420/#specification
|
||||
* for details on namespace packages.
|
||||
*/
|
||||
private predicate isNameSpacePackage(Folder f, string name) {
|
||||
legalShortName(name) and
|
||||
name = f.getStem() and
|
||||
not isRegularPackage(f, name) and
|
||||
// it has to be imported in a file
|
||||
// either in this folder or next to this folder
|
||||
name = moduleImportedInPackage([f, f.getParent()]) and
|
||||
// no sibling regular package
|
||||
// and no sibling module
|
||||
not exists(Folder sibling | sibling.getParent() = f.getParent() |
|
||||
isRegularPackage(sibling.getFolder(name), name)
|
||||
or
|
||||
isPotentialModuleFile(sibling.getAFile(), name)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the folder `f` is a package (either a regular package
|
||||
* or a namespace package) named `name`.
|
||||
*/
|
||||
private predicate isPackage(Folder f, string name) {
|
||||
isRegularPackage(f, name)
|
||||
or
|
||||
isNameSpacePackage(f, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the file `f` is a module named `name`.
|
||||
*/
|
||||
private predicate isModuleFile(File file, string name) {
|
||||
isPotentialModuleFile(file, name) and
|
||||
not isPackage(file.getParent(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the folder `f` is a package named `name`
|
||||
* and does reside inside another package.
|
||||
*/
|
||||
private predicate isOutermostPackage(Folder f, string name) {
|
||||
isPackage(f, name) and
|
||||
not isPackage(f.getParent(), _)
|
||||
}
|
||||
|
||||
/** Gets the name of the module that `c` resolves to, if any. */
|
||||
cached
|
||||
string moduleNameFromFile(Container file) {
|
||||
string moduleNameFromFile(Container c) {
|
||||
// package
|
||||
isOutermostPackage(c, result)
|
||||
or
|
||||
// module
|
||||
isModuleFile(c, result)
|
||||
or
|
||||
Stages::AST::ref() and
|
||||
exists(string basename |
|
||||
basename = moduleNameFromBase(file) and
|
||||
basename = moduleNameFromBase(c) and
|
||||
legalShortName(basename)
|
||||
|
|
||||
result = moduleNameFromFile(file.getParent()) + "." + basename
|
||||
// recursive case
|
||||
result = moduleNameFromFile(c.getParent()) + "." + basename
|
||||
or
|
||||
// If `file` is a transitive import of a file that's executed directly, we allow references
|
||||
// to it by its `basename`.
|
||||
transitively_imported_from_entry_point(file) and
|
||||
transitively_imported_from_entry_point(c) and
|
||||
result = basename
|
||||
)
|
||||
or
|
||||
isPotentialSourcePackage(file) and
|
||||
result = file.getStem() and
|
||||
(
|
||||
not isPotentialSourcePackage(file.getParent()) or
|
||||
not legalShortName(file.getParent().getBaseName())
|
||||
)
|
||||
//
|
||||
// standard library
|
||||
result = c.getStem() and c.getParent() = c.getImportRoot()
|
||||
or
|
||||
result = file.getStem() and file.getParent() = file.getImportRoot()
|
||||
or
|
||||
result = file.getStem() and isStubRoot(file.getParent())
|
||||
result = c.getStem() and isStubRoot(c.getParent())
|
||||
}
|
||||
|
||||
private predicate isStubRoot(Folder f) {
|
||||
|
||||
@@ -1352,7 +1352,10 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
abstract ControlFlowNode getNode();
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
|
||||
|
||||
/** Gets the scope of this node, if any. */
|
||||
abstract Scope getScope();
|
||||
|
||||
/** Gets the location of this dataflow call. */
|
||||
abstract Location getLocation();
|
||||
@@ -1400,7 +1403,7 @@ class NormalCall extends ExtractedDataFlowCall, TNormalCall {
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
|
||||
override Scope getScope() { result = call.getScope() }
|
||||
|
||||
override DataFlowCallable getCallable() { result.(DataFlowFunction).getScope() = target }
|
||||
|
||||
@@ -1450,7 +1453,7 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
|
||||
override Scope getScope() { result = call.getScope() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1474,6 +1477,8 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
|
||||
|
||||
override Scope getScope() { none() }
|
||||
|
||||
override DataFlowCallable getCallable() { none() }
|
||||
|
||||
override ArgumentNode getArgument(ArgumentPosition apos) { none() }
|
||||
|
||||
@@ -279,14 +279,16 @@ class NonSyntheticPostUpdateNode extends PostUpdateNodeImpl, CfgNode {
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
/**
|
||||
* Flow between ESSA variables.
|
||||
* This includes both local and global variables.
|
||||
* Flow comes from definitions, uses and refinements.
|
||||
* A module to compute local flow.
|
||||
*
|
||||
* Flow will generally go from control flow nodes into essa variables at definitions,
|
||||
* and from there via use-use flow to other control flow nodes.
|
||||
*
|
||||
* Some syntaxtic constructs are handled separately.
|
||||
*/
|
||||
// TODO: Consider constraining `nodeFrom` and `nodeTo` to be in the same scope.
|
||||
// If they have different enclosing callables, we get consistency errors.
|
||||
module EssaFlow {
|
||||
predicate essaFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
module LocalFlow {
|
||||
/** Holds if `nodeFrom` is the control flow node defining the essa variable `nodeTo`. */
|
||||
predicate definitionFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Definition
|
||||
// `x = f(42)`
|
||||
// nodeFrom is `f(42)`, cfg node
|
||||
@@ -336,10 +338,37 @@ module EssaFlow {
|
||||
// nodeFrom is `x`, cfgNode
|
||||
// nodeTo is `x`, essa var
|
||||
exists(ParameterDefinition pd |
|
||||
nodeFrom.asCfgNode() = pd.getDefiningNode() and
|
||||
nodeTo.asVar() = pd.getVariable()
|
||||
nodeFrom.(CfgNode).getNode() = pd.getDefiningNode() and
|
||||
nodeTo.(EssaNode).getVar() = pd.getVariable()
|
||||
)
|
||||
}
|
||||
|
||||
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// If expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
|
||||
or
|
||||
// Assignment expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
|
||||
or
|
||||
// boolean inline expressions such as `x or y` or `x and y`
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
|
||||
or
|
||||
// Flow inside an unpacking assignment
|
||||
iterableUnpackingFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow inside a match statement
|
||||
matchFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
|
||||
AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
|
||||
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
|
||||
}
|
||||
|
||||
predicate useUseFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// First use after definition
|
||||
// `y = 42`
|
||||
// `x = f(y)`
|
||||
@@ -353,34 +382,105 @@ module EssaFlow {
|
||||
// nodeFrom is 'y' on first line, cfg node
|
||||
// nodeTo is `y` on second line, cfg node
|
||||
useToNextUse(nodeFrom.asCfgNode(), nodeTo.asCfgNode())
|
||||
or
|
||||
// If expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
|
||||
or
|
||||
// Assignment expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
|
||||
or
|
||||
// boolean inline expressions such as `x or y` or `x and y`
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
|
||||
or
|
||||
// Flow inside an unpacking assignment
|
||||
iterableUnpackingFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
matchFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
|
||||
AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
|
||||
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
IncludePostUpdateFlow<PhaseDependentFlow<definitionFlowStep/2>::step/2>::step(nodeFrom, nodeTo)
|
||||
or
|
||||
IncludePostUpdateFlow<PhaseDependentFlow<expressionFlowStep/2>::step/2>::step(nodeFrom, nodeTo)
|
||||
or
|
||||
// Blindly applying use-use flow can result in a node that steps to itself, for
|
||||
// example in while-loops. To uphold dataflow consistency checks, we don't want
|
||||
// that. However, we do want to allow `[post] n` to `n` (to handle while loops), so
|
||||
// we should only do the filtering after `IncludePostUpdateFlow` has ben applied.
|
||||
IncludePostUpdateFlow<PhaseDependentFlow<useUseFlowStep/2>::step/2>::step(nodeFrom, nodeTo) and
|
||||
nodeFrom != nodeTo
|
||||
}
|
||||
}
|
||||
|
||||
//--------
|
||||
// Local flow
|
||||
//--------
|
||||
/** A module for transforming step relations. */
|
||||
module StepRelationTransformations {
|
||||
/**
|
||||
* Holds if there is a step from `nodeFrom` to `nodeTo` in
|
||||
* the step relation to be transformed.
|
||||
*
|
||||
* This is the input relation to the transformations.
|
||||
*/
|
||||
signature predicate stepSig(Node nodeFrom, Node nodeTo);
|
||||
|
||||
/**
|
||||
* A module to separate import-time from run-time.
|
||||
*
|
||||
* We really have two local flow relations, one for module initialisation time (or _import time_) and one for runtime.
|
||||
* Consider a read from a global variable `x = foo`. At import time there should be a local flow step from `foo` to `x`,
|
||||
* while at runtime there should be a jump step from the module variable corresponding to `foo` to `x`.
|
||||
*
|
||||
* Similarly, for a write `foo = y`, at import time, there is a local flow step from `y` to `foo` while at runtime there
|
||||
* is a jump step from `y` to the module variable corresponding to `foo`.
|
||||
*
|
||||
* We need a way of distinguishing if we are looking at import time or runtime. We have the following helpful facts:
|
||||
* - All top-level executable statements are import time (and import time only)
|
||||
* - All non-top-level code may be executed at runtime (but could also be executed at import time)
|
||||
*
|
||||
* We could write an analysis to determine which functions are called at import time, but until we have that, we will go
|
||||
* with the heuristic that global variables act according to import time rules at top-level program points and according
|
||||
* to runtime rules everywhere else. This will forego some import time local flow but otherwise be consistent.
|
||||
*/
|
||||
module PhaseDependentFlow<stepSig/2 rawStep> {
|
||||
/**
|
||||
* Holds if `node` is found at the top level of a module.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate isTopLevel(Node node) { node.getScope() instanceof Module }
|
||||
|
||||
/** Holds if a step can be taken from `nodeFrom` to `nodeTo` at import time. */
|
||||
predicate importTimeStep(Node nodeFrom, Node nodeTo) {
|
||||
// As a proxy for whether statements can be executed at import time,
|
||||
// we check if they appear at the top level.
|
||||
// This will miss statements inside functions called from the top level.
|
||||
isTopLevel(nodeFrom) and
|
||||
isTopLevel(nodeTo) and
|
||||
rawStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/** Holds if a step can be taken from `nodeFrom` to `nodeTo` at runtime. */
|
||||
predicate runtimeStep(Node nodeFrom, Node nodeTo) {
|
||||
// Anything not at the top level can be executed at runtime.
|
||||
not isTopLevel(nodeFrom) and
|
||||
not isTopLevel(nodeTo) and
|
||||
rawStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a step can be taken from `nodeFrom` to `nodeTo`.
|
||||
*/
|
||||
predicate step(Node nodeFrom, Node nodeTo) {
|
||||
importTimeStep(nodeFrom, nodeTo) or
|
||||
runtimeStep(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module to add steps from post-update nodes.
|
||||
* Whenever there is a step from `x` to `y`,
|
||||
* we add a step from `[post] x` to `y`.
|
||||
*/
|
||||
module IncludePostUpdateFlow<stepSig/2 rawStep> {
|
||||
predicate step(Node nodeFrom, Node nodeTo) {
|
||||
// We either have a raw step from `nodeFrom`...
|
||||
rawStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// ...or we have a raw step from a pre-update node of `nodeFrom`
|
||||
rawStep(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import StepRelationTransformations
|
||||
|
||||
/**
|
||||
* This is the local flow predicate that is used as a building block in global
|
||||
* data flow.
|
||||
@@ -401,69 +501,17 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
* or at runtime when callables in the module are called.
|
||||
*/
|
||||
predicate simpleLocalFlowStepForTypetracking(Node nodeFrom, Node nodeTo) {
|
||||
// If there is local flow out of a node `node`, we want flow
|
||||
// both out of `node` and any post-update node of `node`.
|
||||
exists(Node node |
|
||||
nodeFrom = update(node) and
|
||||
(
|
||||
importTimeLocalFlowStep(node, nodeTo) or
|
||||
runtimeLocalFlowStep(node, nodeTo)
|
||||
)
|
||||
)
|
||||
IncludePostUpdateFlow<PhaseDependentFlow<LocalFlow::localFlowStep/2>::step/2>::step(nodeFrom,
|
||||
nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is found at the top level of a module.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate isTopLevel(Node node) { node.getScope() instanceof Module }
|
||||
|
||||
/** Holds if there is local flow from `nodeFrom` to `nodeTo` at import time. */
|
||||
predicate importTimeLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// As a proxy for whether statements can be executed at import time,
|
||||
// we check if they appear at the top level.
|
||||
// This will miss statements inside functions called from the top level.
|
||||
isTopLevel(nodeFrom) and
|
||||
isTopLevel(nodeTo) and
|
||||
EssaFlow::essaFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/** Holds if there is local flow from `nodeFrom` to `nodeTo` at runtime. */
|
||||
predicate runtimeLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Anything not at the top level can be executed at runtime.
|
||||
not isTopLevel(nodeFrom) and
|
||||
not isTopLevel(nodeTo) and
|
||||
EssaFlow::essaFlowStep(nodeFrom, nodeTo)
|
||||
private predicate summaryLocalStep(Node nodeFrom, Node nodeTo) {
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
|
||||
}
|
||||
|
||||
predicate summaryFlowSteps(Node nodeFrom, Node nodeTo) {
|
||||
// If there is local flow out of a node `node`, we want flow
|
||||
// both out of `node` and any post-update node of `node`.
|
||||
exists(Node node |
|
||||
nodeFrom = update(node) and
|
||||
(
|
||||
importTimeSummaryFlowStep(node, nodeTo) or
|
||||
runtimeSummaryFlowStep(node, nodeTo)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate importTimeSummaryFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// As a proxy for whether statements can be executed at import time,
|
||||
// we check if they appear at the top level.
|
||||
// This will miss statements inside functions called from the top level.
|
||||
isTopLevel(nodeFrom) and
|
||||
isTopLevel(nodeTo) and
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
|
||||
}
|
||||
|
||||
predicate runtimeSummaryFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Anything not at the top level can be executed at runtime.
|
||||
not isTopLevel(nodeFrom) and
|
||||
not isTopLevel(nodeTo) and
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
|
||||
IncludePostUpdateFlow<PhaseDependentFlow<summaryLocalStep/2>::step/2>::step(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/** `ModuleVariable`s are accessed via jump steps at runtime. */
|
||||
@@ -487,15 +535,6 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `result` is either `node`, or the post-update node for `node`.
|
||||
*/
|
||||
private Node update(Node node) {
|
||||
result = node
|
||||
or
|
||||
result.(PostUpdateNode).getPreUpdateNode() = node
|
||||
}
|
||||
|
||||
//--------
|
||||
// Type pruning
|
||||
//--------
|
||||
@@ -807,10 +846,27 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
* ```
|
||||
* data flows from `x` to the attribute `foo` of (the post-update node for) `obj`.
|
||||
*/
|
||||
predicate attributeStoreStep(Node nodeFrom, AttributeContent c, PostUpdateNode nodeTo) {
|
||||
exists(AttrWrite write |
|
||||
write.accesses(nodeTo.getPreUpdateNode(), c.getAttribute()) and
|
||||
nodeFrom = write.getValue()
|
||||
predicate attributeStoreStep(Node nodeFrom, AttributeContent c, Node nodeTo) {
|
||||
exists(Node object |
|
||||
// Normally we target a PostUpdateNode. However, for class definitions the class
|
||||
// is only constructed after evaluating its' entire scope, so in terms of python
|
||||
// evaluations there is no post or pre update nodes, just one node for the class
|
||||
// expression. Therefore we target the class expression directly.
|
||||
//
|
||||
// Note: Due to the way we handle decorators, using a class decorator will result in
|
||||
// there being a post-update node for the class (argument to the decorator). We do
|
||||
// not want to differentiate between these two cases, so still target the class
|
||||
// expression directly.
|
||||
object = nodeTo.(PostUpdateNode).getPreUpdateNode() and
|
||||
not object.asExpr() instanceof ClassExpr
|
||||
or
|
||||
object = nodeTo and
|
||||
object.asExpr() instanceof ClassExpr
|
||||
|
|
||||
exists(AttrWrite write |
|
||||
write.accesses(object, c.getAttribute()) and
|
||||
nodeFrom = write.getValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1005,3 +1061,11 @@ class ContentApprox = Unit;
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
/** Helper for `.getEnclosingCallable`. */
|
||||
DataFlowCallable getCallableScope(Scope s) {
|
||||
result.getScope() = s
|
||||
or
|
||||
not exists(DataFlowCallable c | c.getScope() = s) and
|
||||
result = getCallableScope(s.getEnclosingScope())
|
||||
}
|
||||
|
||||
@@ -117,14 +117,6 @@ newtype TNode =
|
||||
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
|
||||
}
|
||||
|
||||
/** Helper for `Node::getEnclosingCallable`. */
|
||||
private DataFlowCallable getCallableScope(Scope s) {
|
||||
result.getScope() = s
|
||||
or
|
||||
not exists(DataFlowCallable c | c.getScope() = s) and
|
||||
result = getCallableScope(s.getEnclosingScope())
|
||||
}
|
||||
|
||||
private import semmle.python.internal.CachedStages
|
||||
|
||||
/**
|
||||
@@ -400,7 +392,7 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
|
||||
override Scope getScope() { result = mod }
|
||||
|
||||
override string toString() {
|
||||
result = "ModuleVariableNode in " + mod.toString() + " for " + var.getId()
|
||||
result = "ModuleVariableNode in " + concat( | | mod.toString(), ",") + " for " + var.getId()
|
||||
}
|
||||
|
||||
/** Gets the module in which this variable appears. */
|
||||
|
||||
@@ -111,13 +111,13 @@ module ImportResolution {
|
||||
allowedEssaImportStep*(firstDef, lastUseVar) and
|
||||
not allowedEssaImportStep(_, firstDef)
|
||||
|
|
||||
not EssaFlow::defToFirstUse(firstDef, _) and
|
||||
not LocalFlow::defToFirstUse(firstDef, _) and
|
||||
val.asVar() = firstDef
|
||||
or
|
||||
exists(ControlFlowNode mid, ControlFlowNode end |
|
||||
EssaFlow::defToFirstUse(firstDef, mid) and
|
||||
EssaFlow::useToNextUse*(mid, end) and
|
||||
not EssaFlow::useToNextUse(end, _) and
|
||||
LocalFlow::defToFirstUse(firstDef, mid) and
|
||||
LocalFlow::useToNextUse*(mid, end) and
|
||||
not LocalFlow::useToNextUse(end, _) and
|
||||
val.asCfgNode() = end
|
||||
)
|
||||
)
|
||||
@@ -227,7 +227,7 @@ module ImportResolution {
|
||||
*/
|
||||
pragma[inline]
|
||||
private Module getModuleFromName(string name) {
|
||||
isPreferredModuleForName(result.getFile(), name + ["", ".__init__"])
|
||||
isPreferredModuleForName(result.getFile(), [name, name + ".__init__"])
|
||||
}
|
||||
|
||||
/** Gets the module from which attributes are imported by `i`. */
|
||||
|
||||
@@ -73,8 +73,12 @@ signature module Input {
|
||||
}
|
||||
|
||||
// Relating nodes to summaries
|
||||
/** Gets a dataflow node respresenting the argument of `call` indicated by `arg`. */
|
||||
Node argumentOf(Node call, SummaryComponent arg);
|
||||
/**
|
||||
* Gets a dataflow node respresenting the argument of `call` indicated by `arg`.
|
||||
*
|
||||
* Returns the post-update node of the argument when `isPostUpdate` is true.
|
||||
*/
|
||||
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate);
|
||||
|
||||
/** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */
|
||||
Node parameterOf(Node callable, SummaryComponent param);
|
||||
@@ -221,14 +225,18 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
|
||||
/**
|
||||
* Gets a data flow `I::Node` corresponding an argument or return value of `call`,
|
||||
* as specified by `component`.
|
||||
* as specified by `component`. `isOutput` indicates whether the node represents
|
||||
* an output node or an input node.
|
||||
*/
|
||||
bindingset[call, component]
|
||||
private I::Node evaluateSummaryComponentLocal(I::Node call, I::SummaryComponent component) {
|
||||
result = I::argumentOf(call, component)
|
||||
private I::Node evaluateSummaryComponentLocal(
|
||||
I::Node call, I::SummaryComponent component, boolean isOutput
|
||||
) {
|
||||
result = I::argumentOf(call, component, isOutput)
|
||||
or
|
||||
component = I::return() and
|
||||
result = call
|
||||
result = call and
|
||||
isOutput = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,27 +288,40 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private I::Node evaluateSummaryComponentStackLocal(
|
||||
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack
|
||||
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack, boolean isOutput
|
||||
) {
|
||||
exists(I::SummaryComponent component |
|
||||
dependsOnSummaryComponentStackLeaf(callable, component) and
|
||||
stack = I::singleton(component) and
|
||||
call = I::callTo(callable) and
|
||||
result = evaluateSummaryComponentLocal(call, component)
|
||||
result = evaluateSummaryComponentLocal(call, component, isOutput)
|
||||
)
|
||||
or
|
||||
exists(I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail |
|
||||
prev = evaluateSummaryComponentStackLocal(callable, call, tail) and
|
||||
exists(
|
||||
I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail, boolean isOutput0
|
||||
|
|
||||
prev = evaluateSummaryComponentStackLocal(callable, call, tail, isOutput0) and
|
||||
dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
|
||||
pragma[only_bind_out](tail)) and
|
||||
stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
|
||||
|
|
||||
result = I::parameterOf(prev, head)
|
||||
// `Parameter[X]` is only allowed in the output of flow summaries (hence `isOutput = true`),
|
||||
// however the target of the parameter (e.g. `Argument[Y].Parameter[X]`) should be fetched
|
||||
// not from a post-update argument node (hence `isOutput0 = false`)
|
||||
result = I::parameterOf(prev, head) and
|
||||
isOutput0 = false and
|
||||
isOutput = true
|
||||
or
|
||||
result = I::returnOf(prev, head)
|
||||
// `ReturnValue` is only allowed in the input of flow summaries (hence `isOutput = false`),
|
||||
// and the target of the return value (e.g. `Argument[X].ReturnValue`) should be fetched not
|
||||
// from a post-update argument node (hence `isOutput0 = false`)
|
||||
result = I::returnOf(prev, head) and
|
||||
isOutput0 = false and
|
||||
isOutput = false
|
||||
or
|
||||
componentLevelStep(head) and
|
||||
result = prev
|
||||
result = prev and
|
||||
isOutput = isOutput0
|
||||
)
|
||||
}
|
||||
|
||||
@@ -312,8 +333,8 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
|
|
||||
callable.propagatesFlow(input, output, true) and
|
||||
call = I::callTo(callable) and
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -325,8 +346,8 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
hasLoadSummary(callable, content, pragma[only_bind_into](input),
|
||||
pragma[only_bind_into](output)) and
|
||||
call = I::callTo(callable) and
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -338,8 +359,8 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
hasStoreSummary(callable, content, pragma[only_bind_into](input),
|
||||
pragma[only_bind_into](output)) and
|
||||
call = I::callTo(callable) and
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -354,8 +375,8 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
|
||||
pragma[only_bind_into](output)) and
|
||||
call = I::callTo(callable) and
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -369,8 +390,8 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
|
||||
pragma[only_bind_into](output)) and
|
||||
call = I::callTo(callable) and
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -384,8 +405,8 @@ module SummaryFlow<Input I> implements Output<I> {
|
||||
hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
|
||||
pragma[only_bind_into](output)) and
|
||||
call = I::callTo(callable) and
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
|
||||
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
|
||||
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,10 +214,11 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||
predicate return = FlowSummary::SummaryComponent::return/0;
|
||||
|
||||
// Relating nodes to summaries
|
||||
Node argumentOf(Node call, SummaryComponent arg) {
|
||||
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
|
||||
exists(DataFlowDispatch::ParameterPosition pos |
|
||||
arg = FlowSummary::SummaryComponent::argument(pos) and
|
||||
argumentPositionMatch(call, result, pos)
|
||||
argumentPositionMatch(call, result, pos) and
|
||||
isPostUpdate = [false, true] // todo: implement when/if Python uses post-update nodes in type tracking
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
42
python/ql/lib/semmle/python/frameworks/Aiofile.qll
Normal file
42
python/ql/lib/semmle/python/frameworks/Aiofile.qll
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `aiofile` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofile.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `aiofile` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofile.
|
||||
*/
|
||||
private module Aiofile {
|
||||
/**
|
||||
* A call to the `async_open` function or `AIOFile` constructor from `aiofile` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
FileResponseCall() {
|
||||
this = API::moduleImport("aiofile").getMember("async_open").getACall() and
|
||||
methodName = "async_open"
|
||||
or
|
||||
this = API::moduleImport("aiofile").getMember("AIOFile").getACall() and
|
||||
methodName = "AIOFile"
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "file_specifier").asSink() and
|
||||
methodName = "async_open"
|
||||
or
|
||||
result = this.getParameter(0, "filename").asSink() and
|
||||
methodName = "AIOFile"
|
||||
}
|
||||
}
|
||||
}
|
||||
28
python/ql/lib/semmle/python/frameworks/Aiofiles.qll
Normal file
28
python/ql/lib/semmle/python/frameworks/Aiofiles.qll
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `aiofiles` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofiles.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `aiofiles` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofiles.
|
||||
*/
|
||||
private module Aiofiles {
|
||||
/**
|
||||
* A call to the `open` function from `aiofiles` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileResponseCall() { this = API::moduleImport("aiofiles").getMember("open").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
|
||||
}
|
||||
}
|
||||
54
python/ql/lib/semmle/python/frameworks/Anyio.qll
Normal file
54
python/ql/lib/semmle/python/frameworks/Anyio.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `anyio` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/anyio.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `anyio` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/anyio.
|
||||
*/
|
||||
private module Anyio {
|
||||
/**
|
||||
* A call to the `from_path` function from `FileReadStream` or `FileWriteStream` constructors of `anyio.streams.file` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileStreamCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileStreamCall() {
|
||||
this =
|
||||
API::moduleImport("anyio")
|
||||
.getMember("streams")
|
||||
.getMember("file")
|
||||
.getMember(["FileReadStream", "FileWriteStream"])
|
||||
.getMember("from_path")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `Path` constructor from `anyio` as a sink for Filesystem access.
|
||||
*/
|
||||
class PathCall extends FileSystemAccess::Range, API::CallNode {
|
||||
PathCall() { this = API::moduleImport("anyio").getMember("Path").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0).asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `open_file` function from `anyio` as a sink for Filesystem access.
|
||||
*/
|
||||
class OpenFileCall extends FileSystemAccess::Range, API::CallNode {
|
||||
OpenFileCall() { this = API::moduleImport("anyio").getMember("open_file").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
|
||||
}
|
||||
}
|
||||
35
python/ql/lib/semmle/python/frameworks/Baize.qll
Normal file
35
python/ql/lib/semmle/python/frameworks/Baize.qll
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `baize` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/baize.
|
||||
*/
|
||||
|
||||
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
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
|
||||
/**
|
||||
* Provides models for `baize` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/baize.
|
||||
*/
|
||||
module Baize {
|
||||
/**
|
||||
* A call to the `baize.asgi.FileResponse` constructor as a sink for Filesystem access.
|
||||
*
|
||||
* it is not contained to Starlette source code but it is mentioned in documents as an alternative to Starlette FileResponse
|
||||
*/
|
||||
class BaizeFileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
BaizeFileResponseCall() {
|
||||
this = API::moduleImport("baize").getMember("asgi").getMember("FileResponse").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "filepath").asSink()
|
||||
}
|
||||
}
|
||||
}
|
||||
48
python/ql/lib/semmle/python/frameworks/Cherrypy.qll
Normal file
48
python/ql/lib/semmle/python/frameworks/Cherrypy.qll
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `cherrypy` PyPI package.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `cherrypy` PyPI package.
|
||||
* See https://cherrypy.dev/.
|
||||
*/
|
||||
private module Cherrypy {
|
||||
/**
|
||||
* Holds for an instance of `cherrypy.lib.static`
|
||||
*/
|
||||
API::Node libStatic() {
|
||||
result = API::moduleImport("cherrypy").getMember("lib").getMember("static")
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `serve_file` or `serve_download`or `staticfile` functions of `cherrypy.lib.static` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
string funcName;
|
||||
|
||||
FileResponseCall() {
|
||||
this = libStatic().getMember("staticfile").getACall() and
|
||||
funcName = "staticfile"
|
||||
or
|
||||
this = libStatic().getMember("serve_file").getACall() and
|
||||
funcName = "serve_file"
|
||||
or
|
||||
this = libStatic().getMember("serve_download").getACall() and
|
||||
funcName = "serve_download"
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "path").asSink() and funcName = ["serve_download", "serve_file"]
|
||||
or
|
||||
result = this.getParameter(0, "filename").asSink() and
|
||||
funcName = "staticfile"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,6 +128,8 @@ private module CryptodomeModel {
|
||||
this = newCall.getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = newCall }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(cipherName) }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
@@ -181,21 +183,23 @@ private module CryptodomeModel {
|
||||
class CryptodomeGenericSignatureOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::CallCfgNode
|
||||
{
|
||||
API::CallNode newCall;
|
||||
string methodName;
|
||||
string signatureName;
|
||||
|
||||
CryptodomeGenericSignatureOperation() {
|
||||
methodName in ["sign", "verify"] and
|
||||
this =
|
||||
newCall =
|
||||
API::moduleImport(["Crypto", "Cryptodome"])
|
||||
.getMember("Signature")
|
||||
.getMember(signatureName)
|
||||
.getMember("new")
|
||||
.getReturn()
|
||||
.getMember(methodName)
|
||||
.getACall()
|
||||
.getACall() and
|
||||
this = newCall.getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = newCall }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(signatureName)
|
||||
}
|
||||
@@ -221,19 +225,23 @@ private module CryptodomeModel {
|
||||
class CryptodomeGenericHashOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::CallCfgNode
|
||||
{
|
||||
API::CallNode newCall;
|
||||
string hashName;
|
||||
|
||||
CryptodomeGenericHashOperation() {
|
||||
exists(API::Node hashModule |
|
||||
hashModule =
|
||||
API::moduleImport(["Crypto", "Cryptodome"]).getMember("Hash").getMember(hashName)
|
||||
API::moduleImport(["Crypto", "Cryptodome"]).getMember("Hash").getMember(hashName) and
|
||||
newCall = hashModule.getMember("new").getACall()
|
||||
|
|
||||
this = hashModule.getMember("new").getACall()
|
||||
this = newCall
|
||||
or
|
||||
this = hashModule.getMember("new").getReturn().getMember("update").getACall()
|
||||
this = newCall.getReturn().getMember("update").getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = newCall }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
@@ -209,18 +209,18 @@ private module CryptographyModel {
|
||||
class CryptographyGenericCipherOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::MethodCallNode
|
||||
{
|
||||
API::CallNode init;
|
||||
string algorithmName;
|
||||
string modeName;
|
||||
|
||||
CryptographyGenericCipherOperation() {
|
||||
this =
|
||||
cipherInstance(algorithmName, modeName)
|
||||
.getMember(["decryptor", "encryptor"])
|
||||
.getReturn()
|
||||
.getMember(["update", "update_into"])
|
||||
.getACall()
|
||||
init =
|
||||
cipherInstance(algorithmName, modeName).getMember(["decryptor", "encryptor"]).getACall() and
|
||||
this = init.getReturn().getMember(["update", "update_into"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(algorithmName)
|
||||
}
|
||||
@@ -247,19 +247,17 @@ private module CryptographyModel {
|
||||
}
|
||||
|
||||
/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
|
||||
private API::Node hashInstance(string algorithmName) {
|
||||
exists(API::CallNode call | result = call.getReturn() |
|
||||
call =
|
||||
API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("hashes")
|
||||
.getMember("Hash")
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAValueReachableFromSource() in [
|
||||
call.getArg(0), call.getArgByName("algorithm")
|
||||
]
|
||||
)
|
||||
private API::CallNode hashInstance(string algorithmName) {
|
||||
result =
|
||||
API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("hashes")
|
||||
.getMember("Hash")
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAValueReachableFromSource() in [
|
||||
result.getArg(0), result.getArgByName("algorithm")
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,12 +266,16 @@ private module CryptographyModel {
|
||||
class CryptographyGenericHashOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::MethodCallNode
|
||||
{
|
||||
API::CallNode init;
|
||||
string algorithmName;
|
||||
|
||||
CryptographyGenericHashOperation() {
|
||||
this = hashInstance(algorithmName).getMember("update").getACall()
|
||||
init = hashInstance(algorithmName) and
|
||||
this = init.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(algorithmName)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ private module Rsa {
|
||||
class RsaEncryptCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaEncryptCall() { this = API::moduleImport("rsa").getMember("encrypt").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
@@ -54,6 +56,8 @@ private module Rsa {
|
||||
class RsaDecryptCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaDecryptCall() { this = API::moduleImport("rsa").getMember("decrypt").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("crypto")] }
|
||||
@@ -69,6 +73,8 @@ private module Rsa {
|
||||
class RsaSignCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaSignCall() { this = API::moduleImport("rsa").getMember("sign").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
// signature part
|
||||
result.getName() = "RSA"
|
||||
@@ -96,6 +102,8 @@ private module Rsa {
|
||||
class RsaVerifyCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaVerifyCall() { this = API::moduleImport("rsa").getMember("verify").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
// note that technically there is also a hashing operation going on but we don't
|
||||
// know what algorithm is used up front, since it is encoded in the signature
|
||||
@@ -121,6 +129,8 @@ private module Rsa {
|
||||
{
|
||||
RsaComputeHashCall() { this = API::moduleImport("rsa").getMember("compute_hash").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
exists(StrConst str, DataFlow::Node hashNameArg |
|
||||
hashNameArg in [this.getArg(1), this.getArgByName("method_name")] and
|
||||
@@ -144,6 +154,8 @@ private module Rsa {
|
||||
class RsaSignHashCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaSignHashCall() { this = API::moduleImport("rsa").getMember("sign_hash").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
|
||||
42
python/ql/lib/semmle/python/frameworks/Sanic.qll
Normal file
42
python/ql/lib/semmle/python/frameworks/Sanic.qll
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `sanic` PyPI package.
|
||||
* See https://sanic.dev/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `sanic` PyPI package.
|
||||
* See https://sanic.dev/.
|
||||
*/
|
||||
private module Sanic {
|
||||
/**
|
||||
* Provides models for Sanic applications (an instance of `sanic.Sanic`).
|
||||
*/
|
||||
module App {
|
||||
/** Gets a reference to a Sanic application (an instance of `sanic.Sanic`). */
|
||||
API::Node instance() { result = API::moduleImport("sanic").getMember("Sanic").getReturn() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `file` or `file_stream` functions of `sanic.response` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileResponseCall() {
|
||||
this =
|
||||
API::moduleImport("sanic")
|
||||
.getMember("response")
|
||||
.getMember(["file", "file_stream"])
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "location").asSink()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,4 +163,16 @@ module Starlette {
|
||||
|
||||
/** DEPRECATED: Alias for Url */
|
||||
deprecated module URL = Url;
|
||||
|
||||
/**
|
||||
* A call to the `starlette.responses.FileResponse` constructor as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileResponseCall() {
|
||||
this =
|
||||
API::moduleImport("starlette").getMember("responses").getMember("FileResponse").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,6 +1479,26 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `io.FileIO` constructor.
|
||||
* See https://docs.python.org/3/library/io.html#io.FileIO
|
||||
*/
|
||||
private class FileIOCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileIOCall() { this = API::moduleImport("io").getMember("FileIO").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `io.open_code` function.
|
||||
* See https://docs.python.org/3.11/library/io.html#io.open_code
|
||||
*/
|
||||
private class OpenCodeCall extends FileSystemAccess::Range, API::CallNode {
|
||||
OpenCodeCall() { this = API::moduleImport("io").getMember("open_code").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
|
||||
}
|
||||
|
||||
/** Gets a reference to an open file. */
|
||||
private DataFlow::TypeTrackingNode openFile(DataFlow::TypeTracker t, FileSystemAccess openCall) {
|
||||
t.start() and
|
||||
@@ -2747,6 +2767,8 @@ private module StdlibPrivate {
|
||||
exists(this.getParameter(1, "data"))
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "data").asSink() }
|
||||
@@ -2758,12 +2780,16 @@ private module StdlibPrivate {
|
||||
* A hashing operation by using the `update` method on the result of calling the `hashlib.new` function.
|
||||
*/
|
||||
class HashlibNewUpdateCall extends Cryptography::CryptographicOperation::Range, API::CallNode {
|
||||
API::CallNode init;
|
||||
string hashName;
|
||||
|
||||
HashlibNewUpdateCall() {
|
||||
this = hashlibNewCall(hashName).getReturn().getMember("update").getACall()
|
||||
init = hashlibNewCall(hashName) and
|
||||
this = init.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
@@ -2802,7 +2828,14 @@ private module StdlibPrivate {
|
||||
* (such as `hashlib.md5`), by calling its' `update` method.
|
||||
*/
|
||||
class HashlibHashClassUpdateCall extends HashlibGenericHashOperation {
|
||||
HashlibHashClassUpdateCall() { this = hashClass.getReturn().getMember("update").getACall() }
|
||||
API::CallNode init;
|
||||
|
||||
HashlibHashClassUpdateCall() {
|
||||
init = hashClass.getACall() and
|
||||
this = hashClass.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
}
|
||||
@@ -2819,6 +2852,8 @@ private module StdlibPrivate {
|
||||
exists([this.getArg(0), this.getArgByName("string")])
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
result = this.getArg(0)
|
||||
or
|
||||
@@ -2865,6 +2900,8 @@ private module StdlibPrivate {
|
||||
exists(this.getParameter(1, "msg").asSink())
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override API::Node getDigestArg() { result = digestArg }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "msg").asSink() }
|
||||
@@ -2876,12 +2913,16 @@ private module StdlibPrivate {
|
||||
* See https://docs.python.org/3.11/library/hmac.html#hmac.HMAC.update
|
||||
*/
|
||||
class HmacUpdateCall extends HmacCryptographicOperation {
|
||||
API::CallNode init;
|
||||
API::Node digestArg;
|
||||
|
||||
HmacUpdateCall() {
|
||||
this = getHmacConstructorCall(digestArg).getReturn().getMember("update").getACall()
|
||||
init = getHmacConstructorCall(digestArg) and
|
||||
this = init.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override API::Node getDigestArg() { result = digestArg }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(0, "msg").asSink() }
|
||||
@@ -2895,6 +2936,8 @@ private module StdlibPrivate {
|
||||
class HmacDigestCall extends HmacCryptographicOperation {
|
||||
HmacDigestCall() { this = API::moduleImport("hmac").getMember("digest").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override API::Node getDigestArg() { result = this.getParameter(2, "digest") }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "msg").asSink() }
|
||||
|
||||
@@ -17,7 +17,7 @@ extensible predicate sourceModel(string type, string path, string kind);
|
||||
extensible predicate sinkModel(string type, string path, string kind);
|
||||
|
||||
/**
|
||||
* Holds if calls to `(type, path)`, the value referred to by `input`
|
||||
* Holds if in calls to `(type, path)`, the value referred to by `input`
|
||||
* can flow to the value referred to by `output`.
|
||||
*
|
||||
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
|
||||
@@ -25,6 +25,13 @@ extensible predicate sinkModel(string type, string path, string kind);
|
||||
*/
|
||||
extensible predicate summaryModel(string type, string path, string input, string output, string kind);
|
||||
|
||||
/**
|
||||
* Holds if calls to `(type, path)` should be considered neutral. The meaning of this depends on the `kind`.
|
||||
* If `kind` is `summary`, the call does not propagate data flow. If `kind` is `source`, the call is not a source.
|
||||
* If `kind` is `sink`, the call is not a sink.
|
||||
*/
|
||||
extensible predicate neutralModel(string type, string path, string kind);
|
||||
|
||||
/**
|
||||
* Holds if `(type2, path)` should be seen as an instance of `type1`.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,11 @@ extensions:
|
||||
extensible: summaryModel
|
||||
data: []
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/python-all
|
||||
extensible: neutralModel
|
||||
data: []
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/python-all
|
||||
extensible: typeModel
|
||||
|
||||
@@ -40,6 +40,9 @@ module Cryptography {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
|
||||
|
||||
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
|
||||
DataFlow::Node getInitialization() { result = super.getInitialization() }
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
|
||||
@@ -65,6 +68,9 @@ module Cryptography {
|
||||
* extend `CryptographicOperation` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
|
||||
abstract DataFlow::Node getInitialization();
|
||||
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for
|
||||
* Regular Expression Denial-of-Service Attacks. NSS 2013.
|
||||
* (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf)
|
||||
* (https://arxiv.org/abs/1301.0849)
|
||||
* Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression
|
||||
* Exponential Runtime via Substructural Logics. 2014.
|
||||
* (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf)
|
||||
|
||||
@@ -388,6 +388,7 @@ py_extracted_version(int module : @py_Module ref,
|
||||
/* <Field> ClassExpr.bases = 3, expr_list */
|
||||
/* <Field> ClassExpr.keywords = 4, dict_item_list */
|
||||
/* <Field> ClassExpr.inner_scope = 5, Class */
|
||||
/* <Field> ClassExpr.type_parameters = 6, type_parameter_list */
|
||||
|
||||
/* <Field> Compare.location = 0, location */
|
||||
/* <Field> Compare.parenthesised = 1, bool */
|
||||
@@ -458,6 +459,7 @@ py_extracted_version(int module : @py_Module ref,
|
||||
/* <Field> Function.kwarg = 4, expr */
|
||||
/* <Field> Function.body = 5, stmt_list */
|
||||
/* <Field> Function.is_async = 6, bool */
|
||||
/* <Field> Function.type_parameters = 7, type_parameter_list */
|
||||
/* <Parent> Function = FunctionParent */
|
||||
|
||||
/* <Field> FunctionExpr.location = 0, location */
|
||||
@@ -613,6 +615,9 @@ py_extracted_version(int module : @py_Module ref,
|
||||
/* <Field> Num.n = 2, number */
|
||||
/* <Field> Num.text = 3, number */
|
||||
|
||||
/* <Field> ParamSpec.location = 0, location */
|
||||
/* <Field> ParamSpec.name = 1, expr */
|
||||
|
||||
/* <Field> Pass.location = 0, location */
|
||||
|
||||
/* <Field> PlaceHolder.location = 0, location */
|
||||
@@ -702,6 +707,18 @@ py_extracted_version(int module : @py_Module ref,
|
||||
/* <Field> Tuple.ctx = 3, expr_context */
|
||||
/* <Parent> Tuple = ParameterList */
|
||||
|
||||
/* <Field> TypeAlias.location = 0, location */
|
||||
/* <Field> TypeAlias.name = 1, expr */
|
||||
/* <Field> TypeAlias.type_parameters = 2, type_parameter_list */
|
||||
/* <Field> TypeAlias.value = 3, expr */
|
||||
|
||||
/* <Field> TypeVar.location = 0, location */
|
||||
/* <Field> TypeVar.name = 1, expr */
|
||||
/* <Field> TypeVar.bound = 2, expr */
|
||||
|
||||
/* <Field> TypeVarTuple.location = 0, location */
|
||||
/* <Field> TypeVarTuple.name = 1, expr */
|
||||
|
||||
/* <Field> UnaryExpr.location = 0, location */
|
||||
/* <Field> UnaryExpr.parenthesised = 1, bool */
|
||||
/* <Field> UnaryExpr.op = 2, unaryop */
|
||||
@@ -778,6 +795,10 @@ py_extracted_version(int module : @py_Module ref,
|
||||
/* <Parent> StmtList = StmtListParent */
|
||||
/* <Parent> string = StrParent */
|
||||
/* <Parent> StringList = StrListParent */
|
||||
|
||||
/* <Field> TypeParameter.location = 0, location */
|
||||
/* <Parent> TypeParameter = TypeParameterList */
|
||||
/* <Parent> TypeParameterList = TypeParameterListParent */
|
||||
/* <Parent> Unaryop = UnaryExpr */
|
||||
/* <Parent> Variable = VariableParent */
|
||||
py_Classes(unique int id : @py_Class,
|
||||
@@ -894,6 +915,14 @@ py_strs(varchar(1) id : string ref,
|
||||
py_str_lists(unique int id : @py_str_list,
|
||||
unique int parent : @py_str_list_parent ref);
|
||||
|
||||
py_type_parameters(unique int id : @py_type_parameter,
|
||||
int kind: int ref,
|
||||
int parent : @py_type_parameter_list ref,
|
||||
int idx : int ref);
|
||||
|
||||
py_type_parameter_lists(unique int id : @py_type_parameter_list,
|
||||
unique int parent : @py_type_parameter_list_parent ref);
|
||||
|
||||
py_unaryops(unique int id : @py_unaryop,
|
||||
int kind: int ref,
|
||||
unique int parent : @py_UnaryExpr ref);
|
||||
@@ -1029,7 +1058,13 @@ case @py_stmt.kind of
|
||||
| 23 = @py_While
|
||||
| 24 = @py_With
|
||||
| 25 = @py_TemplateWrite
|
||||
| 26 = @py_AnnAssign;
|
||||
| 26 = @py_AnnAssign
|
||||
| 27 = @py_TypeAlias;
|
||||
|
||||
case @py_type_parameter.kind of
|
||||
0 = @py_ParamSpec
|
||||
| 1 = @py_TypeVar
|
||||
| 2 = @py_TypeVarTuple;
|
||||
|
||||
case @py_unaryop.kind of
|
||||
0 = @py_Invert
|
||||
@@ -1043,7 +1078,7 @@ case @py_unaryop.kind of
|
||||
|
||||
@py_arguments_parent = @py_FunctionExpr | @py_Lambda;
|
||||
|
||||
@py_ast_node = @py_Class | @py_Function | @py_Module | @py_StringPart | @py_comprehension | @py_dict_item | @py_expr | @py_pattern | @py_stmt;
|
||||
@py_ast_node = @py_Class | @py_Function | @py_Module | @py_StringPart | @py_comprehension | @py_dict_item | @py_expr | @py_pattern | @py_stmt | @py_type_parameter;
|
||||
|
||||
@py_bool_parent = @py_For | @py_Function | @py_Print | @py_With | @py_expr | @py_pattern;
|
||||
|
||||
@@ -1055,9 +1090,9 @@ case @py_unaryop.kind of
|
||||
|
||||
@py_expr_or_stmt = @py_expr | @py_stmt;
|
||||
|
||||
@py_expr_parent = @py_AnnAssign | @py_Assert | @py_Assign | @py_AssignExpr | @py_Attribute | @py_AugAssign | @py_Await | @py_BinaryExpr | @py_Call | @py_Case | @py_Compare | @py_DictComp | @py_DictUnpacking | @py_ExceptGroupStmt | @py_ExceptStmt | @py_Exec | @py_Expr_stmt | @py_Filter | @py_For | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_GeneratorExp | @py_Guard | @py_If | @py_IfExp | @py_ImportMember | @py_ImportStar | @py_KeyValuePair | @py_ListComp | @py_MatchAsPattern | @py_MatchCapturePattern | @py_MatchClassPattern | @py_MatchKeywordPattern | @py_MatchLiteralPattern | @py_MatchStmt | @py_MatchValuePattern | @py_Print | @py_Raise | @py_Repr | @py_Return | @py_SetComp | @py_Slice | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_TemplateWrite | @py_UnaryExpr | @py_While | @py_With | @py_Yield | @py_YieldFrom | @py_alias | @py_arguments | @py_comprehension | @py_expr_list | @py_keyword | @py_parameter_list;
|
||||
@py_expr_parent = @py_AnnAssign | @py_Assert | @py_Assign | @py_AssignExpr | @py_Attribute | @py_AugAssign | @py_Await | @py_BinaryExpr | @py_Call | @py_Case | @py_Compare | @py_DictComp | @py_DictUnpacking | @py_ExceptGroupStmt | @py_ExceptStmt | @py_Exec | @py_Expr_stmt | @py_Filter | @py_For | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_GeneratorExp | @py_Guard | @py_If | @py_IfExp | @py_ImportMember | @py_ImportStar | @py_KeyValuePair | @py_ListComp | @py_MatchAsPattern | @py_MatchCapturePattern | @py_MatchClassPattern | @py_MatchKeywordPattern | @py_MatchLiteralPattern | @py_MatchStmt | @py_MatchValuePattern | @py_ParamSpec | @py_Print | @py_Raise | @py_Repr | @py_Return | @py_SetComp | @py_Slice | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_TemplateWrite | @py_TypeAlias | @py_TypeVar | @py_TypeVarTuple | @py_UnaryExpr | @py_While | @py_With | @py_Yield | @py_YieldFrom | @py_alias | @py_arguments | @py_comprehension | @py_expr_list | @py_keyword | @py_parameter_list;
|
||||
|
||||
@py_location_parent = @py_DictUnpacking | @py_KeyValuePair | @py_StringPart | @py_comprehension | @py_expr | @py_keyword | @py_pattern | @py_stmt;
|
||||
@py_location_parent = @py_DictUnpacking | @py_KeyValuePair | @py_StringPart | @py_comprehension | @py_expr | @py_keyword | @py_pattern | @py_stmt | @py_type_parameter;
|
||||
|
||||
@py_parameter = @py_Name | @py_Tuple;
|
||||
|
||||
@@ -1073,6 +1108,8 @@ case @py_unaryop.kind of
|
||||
|
||||
@py_str_parent = @py_Attribute | @py_Class | @py_ClassExpr | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_ImportExpr | @py_ImportMember | @py_Module | @py_SpecialOperation | @py_Str | @py_StringPart | @py_TemplateDottedNotation | @py_keyword | @py_str_list;
|
||||
|
||||
@py_type_parameter_list_parent = @py_ClassExpr | @py_Function | @py_TypeAlias;
|
||||
|
||||
@py_variable_parent = @py_Name | @py_PlaceHolder;
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<dbstats>
|
||||
<typesizes><e>
|
||||
<k>@py_ParamSpec</k><v>100</v></e><e>
|
||||
<k>@py_TypeAlias</k><v>100</v></e><e>
|
||||
<k>@py_TypeVar</k><v>100</v></e><e>
|
||||
<k>@py_TypeVarTuple</k><v>100</v></e><e>
|
||||
<k>@py_type_parameter_list</k><v>100</v></e><e>
|
||||
<k>@py_Guard</k><v>100</v></e><e>
|
||||
<k>@py_MatchAsPattern</k><v>100</v></e><e>
|
||||
<k>@py_MatchOrPattern</k><v>100</v></e><e>
|
||||
@@ -7548,6 +7553,44 @@
|
||||
<dependencies/>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>py_type_parameters</name>
|
||||
<cardinality>1000</cardinality>
|
||||
<columnsizes>
|
||||
<e>
|
||||
<k>id</k>
|
||||
<v>1000</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>kind</k>
|
||||
<v>3</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>parent</k>
|
||||
<v>1000</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>idx</k>
|
||||
<v>100</v>
|
||||
</e>
|
||||
</columnsizes>
|
||||
<dependencies/>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>py_type_parameter_lists</name>
|
||||
<cardinality>1000</cardinality>
|
||||
<columnsizes>
|
||||
<e>
|
||||
<k>id</k>
|
||||
<v>1000</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>parent</k>
|
||||
<v>1000</v>
|
||||
</e>
|
||||
</columnsizes>
|
||||
<dependencies/>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>py_pattern_lists</name>
|
||||
<cardinality>1000</cardinality>
|
||||
<columnsizes>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Add support for type parameters and type alias statements
|
||||
compatibility: backwards
|
||||
@@ -1,3 +1,13 @@
|
||||
## 0.9.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added modeling of more `FileSystemAccess` in packages `cherrypy`, `aiofile`, `aiofiles`, `anyio`, `sanic`, `starlette`, `baize`, and `io`. This will mainly affect the _Uncontrolled data used in path expression_ (`py/path-injection`) query.
|
||||
|
||||
## 0.9.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -13,18 +13,15 @@
|
||||
import python
|
||||
import semmle.python.Concepts
|
||||
|
||||
from
|
||||
Cryptography::CryptographicOperation operation, Cryptography::CryptographicAlgorithm algorithm,
|
||||
string msgPrefix
|
||||
from Cryptography::CryptographicOperation operation, string msgPrefix
|
||||
where
|
||||
algorithm = operation.getAlgorithm() and
|
||||
// `Cryptography::HashingAlgorithm` and `Cryptography::PasswordHashingAlgorithm` are
|
||||
// handled by `py/weak-sensitive-data-hashing`
|
||||
algorithm instanceof Cryptography::EncryptionAlgorithm and
|
||||
(
|
||||
exists(Cryptography::EncryptionAlgorithm algorithm | algorithm = operation.getAlgorithm() |
|
||||
algorithm.isWeak() and
|
||||
msgPrefix = "The cryptographic algorithm " + operation.getAlgorithm().getName()
|
||||
msgPrefix = "The cryptographic algorithm " + algorithm.getName()
|
||||
)
|
||||
or
|
||||
operation.getBlockMode().isWeak() and msgPrefix = "The block mode " + operation.getBlockMode()
|
||||
select operation, msgPrefix + " is broken or weak, and should not be used."
|
||||
select operation, "$@ is broken or weak, and should not be used.", operation.getInitialization(),
|
||||
msgPrefix
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.</li>
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Time_complexity">Time complexity</a>.</li>
|
||||
<li>James Kirrage, Asiri Rathnayake, Hayo Thielecke:
|
||||
<a href="http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
|
||||
<a href="https://arxiv.org/abs/1301.0849">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* database. This query counts the lines of code, excluding whitespace or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* telemetry
|
||||
* @id py/summary/lines-of-code
|
||||
*/
|
||||
|
||||
|
||||
1
python/ql/src/Summary/options
Normal file
1
python/ql/src/Summary/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=3
|
||||
3
python/ql/src/change-notes/released/0.9.2.md
Normal file
3
python/ql/src/change-notes/released/0.9.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.9.2
|
||||
|
||||
No user-facing changes.
|
||||
5
python/ql/src/change-notes/released/0.9.3.md
Normal file
5
python/ql/src/change-notes/released/0.9.3.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.9.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added modeling of more `FileSystemAccess` in packages `cherrypy`, `aiofile`, `aiofiles`, `anyio`, `sanic`, `starlette`, `baize`, and `io`. This will mainly affect the _Uncontrolled data used in path expression_ (`py/path-injection`) query.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.1
|
||||
lastReleaseVersion: 0.9.3
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/all-asymmetric-algorithms
|
||||
* @problem.severity error
|
||||
* @preci cbom
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/all-cryptographic-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/all-asymmetric-encryption-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/asymmetric-key-generation
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/authenticated-encryption-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/block-cipher-mode
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/iv-sources
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/unkown-iv-sources
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/elliptic-curve-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/hash-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/key-derivation
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/key-exchange
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/signing-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/symmetric-encryption-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/classic-model/all-cryptographic-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/classic-model/block-cipher-mode
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @kind problem
|
||||
* @id py/quantum-readiness/cbom/classic-model/hash-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.9.2-dev
|
||||
version: 0.9.4-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
| 38 | ControlFlowNode for ExceptStmt | builtin-class AttributeError |
|
||||
| 40 | ControlFlowNode for ExceptStmt | builtin-class IndexError |
|
||||
| 42 | ControlFlowNode for ExceptStmt | builtin-class KeyError |
|
||||
| 57 | ControlFlowNode for ExceptStmt | builtin-class IOError |
|
||||
| 57 | ControlFlowNode for ExceptStmt | builtin-class IOError |
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
| 35 | ControlFlowNode for Attribute | builtin-class AttributeError |
|
||||
| 37 | ControlFlowNode for Raise | builtin-class Exception |
|
||||
| 53 | ControlFlowNode for Attribute() | builtin-class IOError |
|
||||
| 54 | ControlFlowNode for Attribute() | builtin-class IOError |
|
||||
| 54 | ControlFlowNode for Attribute() | builtin-class IOError |
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
| 36 | ControlFlowNode for a() |
|
||||
| 51 | ControlFlowNode for open() |
|
||||
| 56 | ControlFlowNode for Attribute() |
|
||||
| 58 | ControlFlowNode for Attribute() |
|
||||
| 58 | ControlFlowNode for Attribute() |
|
||||
|
||||
1
python/ql/test/2/library-tests/PointsTo/imports2/options
Normal file
1
python/ql/test/2/library-tests/PointsTo/imports2/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=2 --max-import-depth=2 -r package
|
||||
@@ -7,4 +7,4 @@
|
||||
| 36 | class DerivedFromBuiltin | pop |
|
||||
| 36 | class DerivedFromBuiltin | remove |
|
||||
| 36 | class DerivedFromBuiltin | reverse |
|
||||
| 36 | class DerivedFromBuiltin | sort |
|
||||
| 36 | class DerivedFromBuiltin | sort |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| 0 |
|
||||
| 0 |
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
| Module package.helper | 2 | ImportExpr | 0 | bottom | absolute | assistant |
|
||||
| Module package.helper | 3 | ImportExpr | 0 | top | absolute | sys |
|
||||
| Module package.helper | 10 | ImportExpr | 1 | bottom | relative | package |
|
||||
| Module test_star | 1 | ImportExpr | -1 | bottom | absolute | test_package |
|
||||
| Module test_star | 1 | ImportExpr | -1 | bottom | absolute | test_package |
|
||||
|
||||
@@ -54,4 +54,4 @@
|
||||
| Module test_star | p | str b'p' |
|
||||
| Module test_star | q | str b'q' |
|
||||
| Module test_star | r | str b'r' |
|
||||
| Module test_star | sys | Module sys |
|
||||
| Module test_star | sys | Module sys |
|
||||
|
||||
@@ -39,4 +39,4 @@
|
||||
| Module test_star | p |
|
||||
| Module test_star | q |
|
||||
| Module test_star | r |
|
||||
| Module test_star | sys |
|
||||
| Module test_star | sys |
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
| Module test_package.module2 | test_package.module2 |
|
||||
| Module test_package.module3 | test_package.module3 |
|
||||
| Module test_package.module4 | test_package.module4 |
|
||||
| Module test_star | test_star |
|
||||
| Module test_star | test_star |
|
||||
|
||||
1
python/ql/test/2/library-tests/modules/usage/options
Normal file
1
python/ql/test/2/library-tests/modules/usage/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=2 -F script
|
||||
@@ -5,5 +5,4 @@
|
||||
| six.moves.range | builtin-class xrange |
|
||||
| six.moves.urllib | Package six.moves.urllib |
|
||||
| six.moves.urllib.parse | Module six.moves.urllib_parse |
|
||||
| six.moves.urllib.parse.urlsplit | Function urlsplit |
|
||||
| six.moves.zip | Builtin-function zip |
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
| builtin-class type | __dict__ | Property __dict__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __doc__ | Property __doc__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __module__ | Property __module__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __name__ | Property __name__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __name__ | Property __name__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
| 38 | ControlFlowNode for ExceptStmt | builtin-class AttributeError |
|
||||
| 40 | ControlFlowNode for ExceptStmt | builtin-class IndexError |
|
||||
| 42 | ControlFlowNode for ExceptStmt | builtin-class KeyError |
|
||||
| 57 | ControlFlowNode for ExceptStmt | builtin-class OSError |
|
||||
| 57 | ControlFlowNode for ExceptStmt | builtin-class OSError |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user