Merge branch 'main' into py-restframework

This commit is contained in:
Rasmus Wriedt Larsen
2023-11-21 11:57:48 +01:00
3261 changed files with 193905 additions and 90137 deletions

View File

@@ -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

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added support for functions decorated with `contextlib.contextmanager`.

View File

@@ -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.

View 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.

View 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])`.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.11.1
lastReleaseVersion: 0.11.3

View File

@@ -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

View File

@@ -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() }
}

View File

@@ -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 */

View File

@@ -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

View File

@@ -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) {

View File

@@ -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() }

View File

@@ -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())
}

View File

@@ -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. */

View File

@@ -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`. */

View File

@@ -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)
)
}
}

View File

@@ -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
)
}

View 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"
}
}
}

View 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() }
}
}

View 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() }
}
}

View 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()
}
}
}

View 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"
}
}
}

View File

@@ -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")] }

View File

@@ -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)
}

View File

@@ -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() {

View 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()
}
}
}

View File

@@ -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() }
}
}

View File

@@ -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() }

View File

@@ -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`.
*/

View File

@@ -15,6 +15,11 @@ extensions:
extensible: summaryModel
data: []
- addsTo:
pack: codeql/python-all
extensible: neutralModel
data: []
- addsTo:
pack: codeql/python-all
extensible: typeModel

View File

@@ -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();

View File

@@ -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)

View File

@@ -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;

View File

@@ -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

View File

@@ -0,0 +1,2 @@
description: Add support for type parameters and type alias statements
compatibility: backwards

View File

@@ -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.

View File

@@ -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

View File

@@ -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>

View File

@@ -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
*/

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=3

View File

@@ -0,0 +1,3 @@
## 0.9.2
No user-facing changes.

View 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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.9.1
lastReleaseVersion: 0.9.3

View File

@@ -4,7 +4,7 @@
* @kind problem
* @id py/quantum-readiness/cbom/all-asymmetric-algorithms
* @problem.severity error
* @preci cbom
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/all-cryptographic-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/all-asymmetric-encryption-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/asymmetric-key-generation
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/authenticated-encryption-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/block-cipher-mode
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/iv-sources
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/unkown-iv-sources
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/elliptic-curve-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/hash-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/key-derivation
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/key-exchange
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/signing-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/symmetric-encryption-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/classic-model/all-cryptographic-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/classic-model/block-cipher-mode
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -4,7 +4,6 @@
* @kind problem
* @id py/quantum-readiness/cbom/classic-model/hash-algorithms
* @problem.severity error
* @precision high
* @tags cbom
* cryptography
*/

View File

@@ -1,5 +1,5 @@
name: codeql/python-queries
version: 0.9.2-dev
version: 0.9.4-dev
groups:
- python
- queries

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -3,4 +3,4 @@
| 36 | ControlFlowNode for a() |
| 51 | ControlFlowNode for open() |
| 56 | ControlFlowNode for Attribute() |
| 58 | ControlFlowNode for Attribute() |
| 58 | ControlFlowNode for Attribute() |

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=2 --max-import-depth=2 -r package

View File

@@ -7,4 +7,4 @@
| 36 | class DerivedFromBuiltin | pop |
| 36 | class DerivedFromBuiltin | remove |
| 36 | class DerivedFromBuiltin | reverse |
| 36 | class DerivedFromBuiltin | sort |
| 36 | class DerivedFromBuiltin | sort |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -39,4 +39,4 @@
| Module test_star | p |
| Module test_star | q |
| Module test_star | r |
| Module test_star | sys |
| Module test_star | sys |

View File

@@ -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 |

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=2 -F script

View File

@@ -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 |

View File

@@ -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__ |

View File

@@ -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