Merge branch 'github:main' into jorgectf/python/deserialization

This commit is contained in:
Jorge
2022-01-31 17:48:35 +01:00
committed by GitHub
3887 changed files with 317569 additions and 114448 deletions

View File

@@ -0,0 +1,4 @@
lgtm,codescanning
* A new query, `py/bad-tag-filter`, has been added to the query suite,
highlighting regular expressions that only match a subset of the HTML tags
it is supposed to match.

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* Added modeling of `asyncpg` for sinks executing SQL and/or accessing the file system.
* Corrected the API graph, such that all awaited values now are referred to via `getAwaited`.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Added modeling of `aiomysql` for sinks executing SQL

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Added modeling of sources/sinks when using FastAPI to create web servers.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Added modeling of the `send_from_directory` and `send_file` functions from the `flask` PyPI package, resulting in additional sinks for the _Uncontrolled data used in path expression_ (`py/path-injection`) query. This addition was originally [submitted as an external contribution by @porcupineyhairs](https://github.com/github/codeql/pull/6330).

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* The query "Inefficient regular expression" (`py/redos`) has been promoted from experimental to the main query pack. Its results will now appear by default.
* The query "Polynomial regular expression used on uncontrolled data" (`py/polynomial-redos`) has been promoted from experimental to the main query pack. Its results will now appear by default.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Added modeling of HTTP requests and responses when using the Django REST Framework (`djangorestframework` PyPI package), which leads to additional remote flow sources.

View File

@@ -1,4 +1,6 @@
name: codeql/python-examples
version: 0.0.2
groups:
- python
- examples
dependencies:
codeql/python-all: "*"
codeql/python-all: "*"

View File

@@ -0,0 +1,25 @@
## 0.0.7
## 0.0.6
## 0.0.5
### Minor Analysis Improvements
* Added modeling of many functions from the `os` module that uses file system paths, such as `os.stat`, `os.chdir`, `os.mkdir`, and so on.
* Added modeling of the `tempfile` module for creating temporary files and directories, such as the functions `tempfile.NamedTemporaryFile` and `tempfile.TemporaryDirectory`.
* Extended the modeling of FastAPI such that custom subclasses of `fastapi.APIRouter` are recognized.
* Extended the modeling of FastAPI such that `fastapi.responses.FileResponse` are considered `FileSystemAccess`.
* Added modeling of the `posixpath`, `ntpath`, and `genericpath` modules for path operations (although these are not supposed to be used), resulting in new sinks.
* Added modeling of `wsgiref.simple_server` applications, leading to new remote flow sources.
## 0.0.4
### Major Analysis Improvements
* Added modeling of `os.stat`, `os.lstat`, `os.statvfs`, `os.fstat`, and `os.fstatvfs`, which are new sinks for the _Uncontrolled data used in path expression_ (`py/path-injection`) query.
* Added modeling of the `posixpath`, `ntpath`, and `genericpath` modules for path operations (although these are not supposed to be used), resulting in new sinks for the _Uncontrolled data used in path expression_ (`py/path-injection`) query.
* Added modeling of `wsgiref.simple_server` applications, leading to new remote flow sources.
* Added modeling of `aiopg` for sinks executing SQL.
* Added modeling of HTTP requests and responses when using `flask_admin` (`Flask-Admin` PyPI package), which leads to additional remote flow sources.
* Added modeling of the PyPI package `toml`, which provides encoding/decoding of TOML documents, leading to new taint-tracking steps.

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `codeql/python-upgrades` CodeQL pack has been removed. All upgrades scripts have been merged into the `codeql/python-all` CodeQL pack.

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* Moved the files defining regex injection configuration and customization, instead of `import semmle.python.security.injection.RegexInjection` please use `import semmle.python.security.dataflow.RegexInjection` (the same for `RegexInjectionCustomizations`).

View File

@@ -0,0 +1,10 @@
## 0.0.4
### Major Analysis Improvements
* Added modeling of `os.stat`, `os.lstat`, `os.statvfs`, `os.fstat`, and `os.fstatvfs`, which are new sinks for the _Uncontrolled data used in path expression_ (`py/path-injection`) query.
* Added modeling of the `posixpath`, `ntpath`, and `genericpath` modules for path operations (although these are not supposed to be used), resulting in new sinks for the _Uncontrolled data used in path expression_ (`py/path-injection`) query.
* Added modeling of `wsgiref.simple_server` applications, leading to new remote flow sources.
* Added modeling of `aiopg` for sinks executing SQL.
* Added modeling of HTTP requests and responses when using `flask_admin` (`Flask-Admin` PyPI package), which leads to additional remote flow sources.
* Added modeling of the PyPI package `toml`, which provides encoding/decoding of TOML documents, leading to new taint-tracking steps.

View File

@@ -0,0 +1,10 @@
## 0.0.5
### Minor Analysis Improvements
* Added modeling of many functions from the `os` module that uses file system paths, such as `os.stat`, `os.chdir`, `os.mkdir`, and so on.
* Added modeling of the `tempfile` module for creating temporary files and directories, such as the functions `tempfile.NamedTemporaryFile` and `tempfile.TemporaryDirectory`.
* Extended the modeling of FastAPI such that custom subclasses of `fastapi.APIRouter` are recognized.
* Extended the modeling of FastAPI such that `fastapi.responses.FileResponse` are considered `FileSystemAccess`.
* Added modeling of the `posixpath`, `ntpath`, and `genericpath` modules for path operations (although these are not supposed to be used), resulting in new sinks.
* Added modeling of `wsgiref.simple_server` applications, leading to new remote flow sources.

View File

@@ -0,0 +1 @@
## 0.0.6

View File

@@ -0,0 +1 @@
## 0.0.7

View File

@@ -0,0 +1,2 @@
---
lastReleaseVersion: 0.0.7

View File

@@ -0,0 +1,6 @@
description: Add new statements and expressions for the match syntax.
compatibility: backwards
py_exprs.rel: run py_exprs.qlo
py_stmts.rel: run py_stmts.qlo
py_patterns.rel: delete
py_patterns_lists.rel: delete

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
// First we need to wrap some database types
class Location extends @location {
/** Gets the start line of this location */
int getStartLine() {
locations_default(this, _, result, _, _, _) or
locations_ast(this, _, result, _, _, _)
}
string toString() { result = "<some file>" + ":" + this.getStartLine().toString() }
}
class Expr_ extends @py_expr {
string toString() { result = "Expr" }
Location getLocation() { py_locations(result, this) }
}
class ExprParent_ extends @py_expr_parent {
string toString() { result = "ExprParent" }
}
/**
* New kinds have been inserted such that
* `@py_Name` which used to have index 18 now has index 19.
* Entries with lower indices are unchanged.
*/
bindingset[new_index]
int old_index(int new_index) {
if new_index < 18 then result = new_index else result + (19 - 18) = new_index
}
// The schema for py_exprs is:
//
// py_exprs(unique int id : @py_expr,
// int kind: int ref,
// int parent : @py_expr_parent ref,
// int idx : int ref);
from Expr_ expr, int new_kind, ExprParent_ parent, int idx, int old_kind
where
py_exprs(expr, new_kind, parent, idx) and
old_kind = old_index(new_kind)
select expr, old_kind, parent, idx

View File

@@ -0,0 +1,42 @@
// First we need to wrap some database types
class Location extends @location {
/** Gets the start line of this location */
int getStartLine() {
locations_default(this, _, result, _, _, _) or
locations_ast(this, _, result, _, _, _)
}
string toString() { result = "<some file>" + ":" + this.getStartLine().toString() }
}
class Stmt_ extends @py_stmt {
string toString() { result = "Stmt" }
Location getLocation() { py_locations(result, this) }
}
class StmtList_ extends @py_stmt_list {
string toString() { result = "StmtList" }
}
/**
* New kinds have been inserted such that
* `@py_Nonlocal` which used to have index 14 now has index 16.
* Entries with lower indices are unchanged.
*/
bindingset[new_index]
int old_index(int new_index) {
if new_index < 14 then result = new_index else result + (16 - 14) = new_index
}
// The schema for py_stmts is:
//
// py_stmts(unique int id : @py_stmt,
// int kind: int ref,
// int parent : @py_stmt_list ref,
// int idx : int ref);
from Stmt_ expr, int new_kind, StmtList_ parent, int idx, int old_kind
where
py_stmts(expr, new_kind, parent, idx) and
old_kind = old_index(new_kind)
select expr, old_kind, parent, idx

View File

@@ -10,6 +10,7 @@ import semmle.python.Class
import semmle.python.Import
import semmle.python.Stmts
import semmle.python.Exprs
import semmle.python.Patterns
import semmle.python.Keywords
import semmle.python.Comprehensions
import semmle.python.Flow

View File

@@ -1,7 +1,7 @@
name: codeql/python-all
version: 0.0.2
version: 0.0.8-dev
groups: python
dbscheme: semmlecode.python.dbscheme
extractor: python
library: true
dependencies:
codeql/python-upgrades: 0.0.2
upgrades: upgrades

View File

@@ -304,6 +304,8 @@ module API {
* API graph node for the prefix `foo`), in accordance with the usual semantics of Python.
*/
private import semmle.python.internal.Awaited
cached
newtype TApiNode =
/** The root of the API graph. */
@@ -356,134 +358,26 @@ module API {
)
}
/** Gets the name of a known built-in. */
private string getBuiltInName() {
// These lists were created by inspecting the `builtins` and `__builtin__` modules in
// Python 3 and 2 respectively, using the `dir` built-in.
// Built-in functions and exceptions shared between Python 2 and 3
result in [
"abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", "classmethod",
"compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "filter",
"float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex",
"id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map",
"max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print",
"property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted",
"staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__",
// Exceptions
"ArithmeticError", "AssertionError", "AttributeError", "BaseException", "BufferError",
"BytesWarning", "DeprecationWarning", "EOFError", "EnvironmentError", "Exception",
"FloatingPointError", "FutureWarning", "GeneratorExit", "IOError", "ImportError",
"ImportWarning", "IndentationError", "IndexError", "KeyError", "KeyboardInterrupt",
"LookupError", "MemoryError", "NameError", "NotImplemented", "NotImplementedError",
"OSError", "OverflowError", "PendingDeprecationWarning", "ReferenceError", "RuntimeError",
"RuntimeWarning", "StandardError", "StopIteration", "SyntaxError", "SyntaxWarning",
"SystemError", "SystemExit", "TabError", "TypeError", "UnboundLocalError",
"UnicodeDecodeError", "UnicodeEncodeError", "UnicodeError", "UnicodeTranslateError",
"UnicodeWarning", "UserWarning", "ValueError", "Warning", "ZeroDivisionError",
// Added for compatibility
"exec"
]
or
// Built-in constants shared between Python 2 and 3
result in ["False", "True", "None", "NotImplemented", "Ellipsis", "__debug__"]
or
// Python 3 only
result in [
"ascii", "breakpoint", "bytes", "exec",
// Exceptions
"BlockingIOError", "BrokenPipeError", "ChildProcessError", "ConnectionAbortedError",
"ConnectionError", "ConnectionRefusedError", "ConnectionResetError", "FileExistsError",
"FileNotFoundError", "InterruptedError", "IsADirectoryError", "ModuleNotFoundError",
"NotADirectoryError", "PermissionError", "ProcessLookupError", "RecursionError",
"ResourceWarning", "StopAsyncIteration", "TimeoutError"
]
or
// Python 2 only
result in [
"basestring", "cmp", "execfile", "file", "long", "raw_input", "reduce", "reload",
"unichr", "unicode", "xrange"
]
}
/**
* Gets a data flow node that is likely to refer to a built-in with the name `name`.
*
* Currently this is an over-approximation, and may not account for things like overwriting a
* built-in with a different value.
*/
private DataFlow::Node likely_builtin(string name) {
exists(Module m |
result.asCfgNode() =
any(NameNode n |
possible_builtin_accessed_in_module(n, name, m) and
not possible_builtin_defined_in_module(name, m)
)
)
}
/**
* Holds if a global variable called `name` (which is also the name of a built-in) is assigned
* a value in the module `m`.
*/
private predicate possible_builtin_defined_in_module(string name, Module m) {
global_name_defined_in_module(name, m) and
name = getBuiltInName()
}
/**
* Holds if `n` is an access of a global variable called `name` (which is also the name of a
* built-in) inside the module `m`.
*/
private predicate possible_builtin_accessed_in_module(NameNode n, string name, Module m) {
n.isGlobal() and
n.isLoad() and
name = n.getId() and
name = getBuiltInName() and
m = n.getEnclosingModule()
}
/**
* Holds if `n` is an access of a variable called `name` (which is _not_ the name of a
* built-in, and which is _not_ a global defined in the enclosing module) inside the scope `s`.
*/
private predicate name_possibly_defined_in_import_star(NameNode n, string name, Scope s) {
n.isLoad() and
name = n.getId() and
// Not already defined in an enclosing scope.
not exists(LocalVariable v |
v.getId() = name and v.getScope() = n.getScope().getEnclosingScope*()
) and
not name = getBuiltInName() and
s = n.getScope().getEnclosingScope*() and
exists(potential_import_star_base(s)) and
not global_name_defined_in_module(name, n.getEnclosingModule())
}
/** Holds if a global variable called `name` is assigned a value in the module `m`. */
private predicate global_name_defined_in_module(string name, Module m) {
exists(NameNode n |
not exists(LocalVariable v | n.defines(v)) and
n.isStore() and
name = n.getId() and
m = n.getEnclosingModule()
)
}
private import semmle.python.dataflow.new.internal.Builtins
private import semmle.python.dataflow.new.internal.ImportStar
/**
* Gets the API graph node for all modules imported with `from ... import *` inside the scope `s`.
*
* For example, given
*
* `from foo.bar import *`
* ```python
* from foo.bar import *
* ```
*
* this would be the API graph node with the path
*
* `moduleImport("foo").getMember("bar")`
*/
private TApiNode potential_import_star_base(Scope s) {
exists(DataFlow::Node ref |
ref.asCfgNode() = any(ImportStarNode n | n.getScope() = s).getModule() and
use(result, ref)
exists(DataFlow::Node n |
n.asCfgNode() = ImportStar::potentialImportStarBase(s) and
use(result, n)
)
}
@@ -518,24 +412,23 @@ module API {
)
or
// awaiting
exists(Await await, DataFlow::Node awaitedValue |
exists(DataFlow::Node awaitedValue |
lbl = Label::await() and
ref.asExpr() = await and
await.getValue() = awaitedValue.asExpr() and
ref = awaited(awaitedValue) and
pred.flowsTo(awaitedValue)
)
)
or
// Built-ins, treated as members of the module `builtins`
base = MkModuleImport("builtins") and
lbl = Label::member(any(string name | ref = likely_builtin(name)))
lbl = Label::member(any(string name | ref = Builtins::likelyBuiltin(name)))
or
// Unknown variables that may belong to a module imported with `import *`
exists(Scope s |
base = potential_import_star_base(s) and
lbl =
Label::member(any(string name |
name_possibly_defined_in_import_star(ref.asCfgNode(), name, s)
ImportStar::namePossiblyDefinedInImportStar(ref.asCfgNode(), name, s)
))
)
}

View File

@@ -82,6 +82,12 @@ library class StrListParent extends StrListParent_ { }
/** Internal implementation class */
library class ExprParent extends ExprParent_ { }
/** Internal implementation class */
class PatternListParent extends PatternListParent_ { }
/** Internal implementation class */
library class PatternParent extends PatternParent_ { }
library class DictItem extends DictItem_, AstNode {
override string toString() { result = DictItem_.super.toString() }
@@ -162,6 +168,9 @@ class ExprList extends ExprList_ {
/* syntax: Expr, ... */
}
/** A list of patterns */
class PatternList extends PatternList_ { }
library class DictItemList extends DictItemList_ { }
library class DictItemListParent extends DictItemListParent_ { }

View File

@@ -218,6 +218,26 @@ library class Call_ extends @py_Call, Expr {
override string toString() { result = "Call" }
}
/** INTERNAL: See the class `Case` for further information. */
library class Case_ extends @py_Case, Stmt {
/** Gets the pattern of this case statement. */
Pattern getPattern() { py_patterns(result, _, this, 1) }
/** Gets the guard of this case statement. */
Expr getGuard() { py_exprs(result, _, this, 2) }
/** Gets the body of this case statement. */
StmtList getBody() { py_stmt_lists(result, this, 3) }
/** Gets the nth statement of this case statement. */
Stmt getStmt(int index) { result = this.getBody().getItem(index) }
/** Gets a statement of this case statement. */
Stmt getAStmt() { result = this.getBody().getAnItem() }
override string toString() { result = "Case" }
}
/** INTERNAL: See the class `Class` for further information. */
library class Class_ extends @py_Class {
/** Gets the name of this class. */
@@ -232,6 +252,7 @@ library class Class_ extends @py_Class {
/** Gets a statement of this class. */
Stmt getAStmt() { result = this.getBody().getAnItem() }
/** Gets a parent of this class */
ClassExpr getParent() { py_Classes(this, result) }
/** Gets a textual representation of this element. */
@@ -513,6 +534,7 @@ library class Function_ extends @py_Function {
/** Whether the async property of this function is true. */
predicate isAsync() { py_bools(this, 6) }
/** Gets a parent of this function */
FunctionParent getParent() { py_Functions(this, result) }
/** Gets a textual representation of this element. */
@@ -577,6 +599,14 @@ library class GtE_ extends @py_GtE, Cmpop {
override string toString() { result = "GtE" }
}
/** INTERNAL: See the class `Guard` for further information. */
library class Guard_ extends @py_Guard, Expr {
/** Gets the test of this guard expression. */
Expr getTest() { py_exprs(result, _, this, 2) }
override string toString() { result = "Guard" }
}
/** INTERNAL: See the class `If` for further information. */
library class If_ extends @py_If, Stmt {
/** Gets the test of this if statement. */
@@ -790,6 +820,172 @@ library class MatMult_ extends @py_MatMult, Operator {
override string toString() { result = "MatMult" }
}
/** INTERNAL: See the class `MatchStmt` for further information. */
library class MatchStmt_ extends @py_MatchStmt, Stmt {
/** Gets the subject of this match statement. */
Expr getSubject() { py_exprs(result, _, this, 1) }
/** Gets the cases of this match statement. */
StmtList getCases() { py_stmt_lists(result, this, 2) }
/** Gets the nth case of this match statement. */
Stmt getCase(int index) { result = this.getCases().getItem(index) }
/** Gets a case of this match statement. */
Stmt getACase() { result = this.getCases().getAnItem() }
override string toString() { result = "MatchStmt" }
}
/** INTERNAL: See the class `MatchAsPattern` for further information. */
library class MatchAsPattern_ extends @py_MatchAsPattern, Pattern {
/** Gets the pattern of this matchaspattern pattern. */
Pattern getPattern() { py_patterns(result, _, this, 2) }
/** Gets the alias of this matchaspattern pattern. */
Expr getAlias() { py_exprs(result, _, this, 3) }
override string toString() { result = "MatchAsPattern" }
}
/** INTERNAL: See the class `MatchCapturePattern` for further information. */
library class MatchCapturePattern_ extends @py_MatchCapturePattern, Pattern {
/** Gets the variable of this matchcapturepattern pattern. */
Expr getVariable() { py_exprs(result, _, this, 2) }
override string toString() { result = "MatchCapturePattern" }
}
/** INTERNAL: See the class `MatchClassPattern` for further information. */
library class MatchClassPattern_ extends @py_MatchClassPattern, Pattern {
/** Gets the class of this matchclasspattern pattern. */
Expr getClass() { py_exprs(result, _, this, 2) }
/** Gets the class_name of this matchclasspattern pattern. */
Expr getClassName() { py_exprs(result, _, this, 3) }
/** Gets the positional of this matchclasspattern pattern. */
PatternList getPositional() { py_pattern_lists(result, this, 4) }
/** Gets the nth positional of this matchclasspattern pattern. */
Pattern getPositional(int index) { result = this.getPositional().getItem(index) }
/** Gets a positional of this matchclasspattern pattern. */
Pattern getAPositional() { result = this.getPositional().getAnItem() }
/** Gets the keyword of this matchclasspattern pattern. */
PatternList getKeyword() { py_pattern_lists(result, this, 5) }
/** Gets the nth keyword of this matchclasspattern pattern. */
Pattern getKeyword(int index) { result = this.getKeyword().getItem(index) }
/** Gets a keyword of this matchclasspattern pattern. */
Pattern getAKeyword() { result = this.getKeyword().getAnItem() }
override string toString() { result = "MatchClassPattern" }
}
/** INTERNAL: See the class `MatchDoubleStarPattern` for further information. */
library class MatchDoubleStarPattern_ extends @py_MatchDoubleStarPattern, Pattern {
/** Gets the target of this matchdoublestarpattern pattern. */
Pattern getTarget() { py_patterns(result, _, this, 2) }
override string toString() { result = "MatchDoubleStarPattern" }
}
/** INTERNAL: See the class `MatchKeyValuePattern` for further information. */
library class MatchKeyValuePattern_ extends @py_MatchKeyValuePattern, Pattern {
/** Gets the key of this matchkeyvaluepattern pattern. */
Pattern getKey() { py_patterns(result, _, this, 2) }
/** Gets the value of this matchkeyvaluepattern pattern. */
Pattern getValue() { py_patterns(result, _, this, 3) }
override string toString() { result = "MatchKeyValuePattern" }
}
/** INTERNAL: See the class `MatchKeywordPattern` for further information. */
library class MatchKeywordPattern_ extends @py_MatchKeywordPattern, Pattern {
/** Gets the attribute of this matchkeywordpattern pattern. */
Expr getAttribute() { py_exprs(result, _, this, 2) }
/** Gets the value of this matchkeywordpattern pattern. */
Pattern getValue() { py_patterns(result, _, this, 3) }
override string toString() { result = "MatchKeywordPattern" }
}
/** INTERNAL: See the class `MatchLiteralPattern` for further information. */
library class MatchLiteralPattern_ extends @py_MatchLiteralPattern, Pattern {
/** Gets the literal of this matchliteralpattern pattern. */
Expr getLiteral() { py_exprs(result, _, this, 2) }
override string toString() { result = "MatchLiteralPattern" }
}
/** INTERNAL: See the class `MatchMappingPattern` for further information. */
library class MatchMappingPattern_ extends @py_MatchMappingPattern, Pattern {
/** Gets the mappings of this matchmappingpattern pattern. */
PatternList getMappings() { py_pattern_lists(result, this, 2) }
/** Gets the nth mapping of this matchmappingpattern pattern. */
Pattern getMapping(int index) { result = this.getMappings().getItem(index) }
/** Gets a mapping of this matchmappingpattern pattern. */
Pattern getAMapping() { result = this.getMappings().getAnItem() }
override string toString() { result = "MatchMappingPattern" }
}
/** INTERNAL: See the class `MatchOrPattern` for further information. */
library class MatchOrPattern_ extends @py_MatchOrPattern, Pattern {
/** Gets the patterns of this matchorpattern pattern. */
PatternList getPatterns() { py_pattern_lists(result, this, 2) }
/** Gets the nth pattern of this matchorpattern pattern. */
Pattern getPattern(int index) { result = this.getPatterns().getItem(index) }
/** Gets a pattern of this matchorpattern pattern. */
Pattern getAPattern() { result = this.getPatterns().getAnItem() }
override string toString() { result = "MatchOrPattern" }
}
/** INTERNAL: See the class `MatchSequencePattern` for further information. */
library class MatchSequencePattern_ extends @py_MatchSequencePattern, Pattern {
/** Gets the patterns of this matchsequencepattern pattern. */
PatternList getPatterns() { py_pattern_lists(result, this, 2) }
/** Gets the nth pattern of this matchsequencepattern pattern. */
Pattern getPattern(int index) { result = this.getPatterns().getItem(index) }
/** Gets a pattern of this matchsequencepattern pattern. */
Pattern getAPattern() { result = this.getPatterns().getAnItem() }
override string toString() { result = "MatchSequencePattern" }
}
/** INTERNAL: See the class `MatchStarPattern` for further information. */
library class MatchStarPattern_ extends @py_MatchStarPattern, Pattern {
/** Gets the target of this matchstarpattern pattern. */
Pattern getTarget() { py_patterns(result, _, this, 2) }
override string toString() { result = "MatchStarPattern" }
}
/** INTERNAL: See the class `MatchValuePattern` for further information. */
library class MatchValuePattern_ extends @py_MatchValuePattern, Pattern {
/** Gets the value of this matchvaluepattern pattern. */
Expr getValue() { py_exprs(result, _, this, 2) }
override string toString() { result = "MatchValuePattern" }
}
/** INTERNAL: See the class `MatchWildcardPattern` for further information. */
library class MatchWildcardPattern_ extends @py_MatchWildcardPattern, Pattern {
override string toString() { result = "MatchWildcardPattern" }
}
/** INTERNAL: See the class `Mod` for further information. */
library class Mod_ extends @py_Mod, Operator {
override string toString() { result = "Mod" }
@@ -1073,6 +1269,7 @@ library class StringPart_ extends @py_StringPart {
/** Gets the location of this implicitly concatenated part. */
Location getLocation() { py_locations(result, this) }
/** Gets a parent of this implicitly concatenated part */
StringPartList getParent() { py_StringParts(this, result, _) }
/** Gets a textual representation of this element. */
@@ -1081,6 +1278,7 @@ library class StringPart_ extends @py_StringPart {
/** INTERNAL: See the class `StringPartList` for further information. */
library class StringPartList_ extends @py_StringPart_list {
/** Gets a parent of this implicitly concatenated part list */
BytesOrStr getParent() { py_StringPart_lists(this, result) }
/** Gets an item of this implicitly concatenated part list */
@@ -1288,6 +1486,7 @@ library class Alias_ extends @py_alias {
/** Gets the name of this alias. */
Expr getAsname() { py_exprs(result, _, this, 1) }
/** Gets a parent of this alias */
AliasList getParent() { py_aliases(this, result, _) }
/** Gets a textual representation of this element. */
@@ -1296,6 +1495,7 @@ library class Alias_ extends @py_alias {
/** INTERNAL: See the class `AliasList` for further information. */
library class AliasList_ extends @py_alias_list {
/** Gets a parent of this alias list */
Import getParent() { py_alias_lists(this, result) }
/** Gets an item of this alias list */
@@ -1352,6 +1552,7 @@ library class Arguments_ extends @py_arguments {
/** Gets a keyword-only annotation of this parameters definition. */
Expr getAKwAnnotation() { result = this.getKwAnnotations().getAnItem() }
/** Gets a parent of this parameters definition */
ArgumentsParent getParent() { py_arguments(this, result) }
/** Gets a textual representation of this element. */
@@ -1378,6 +1579,7 @@ library class BoolParent_ extends @py_bool_parent {
/** INTERNAL: See the class `Boolop` for further information. */
library class Boolop_ extends @py_boolop {
/** Gets a parent of this boolean operator */
BoolExpr getParent() { py_boolops(this, _, result) }
/** Gets a textual representation of this element. */
@@ -1386,6 +1588,7 @@ library class Boolop_ extends @py_boolop {
/** INTERNAL: See the class `Cmpop` for further information. */
library class Cmpop_ extends @py_cmpop {
/** Gets a parent of this comparison operator */
CmpopList getParent() { py_cmpops(this, _, result, _) }
/** Gets a textual representation of this element. */
@@ -1394,6 +1597,7 @@ library class Cmpop_ extends @py_cmpop {
/** INTERNAL: See the class `CmpopList` for further information. */
library class CmpopList_ extends @py_cmpop_list {
/** Gets a parent of this comparison operator list */
Compare getParent() { py_cmpop_lists(this, result) }
/** Gets an item of this comparison operator list */
@@ -1426,6 +1630,7 @@ library class Comprehension_ extends @py_comprehension {
/** Gets a condition of this comprehension. */
Expr getAnIf() { result = this.getIfs().getAnItem() }
/** Gets a parent of this comprehension */
ComprehensionList getParent() { py_comprehensions(this, result, _) }
/** Gets a textual representation of this element. */
@@ -1434,6 +1639,7 @@ library class Comprehension_ extends @py_comprehension {
/** INTERNAL: See the class `ComprehensionList` for further information. */
library class ComprehensionList_ extends @py_comprehension_list {
/** Gets a parent of this comprehension list */
ListComp getParent() { py_comprehension_lists(this, result) }
/** Gets an item of this comprehension list */
@@ -1448,6 +1654,7 @@ library class ComprehensionList_ extends @py_comprehension_list {
/** INTERNAL: See the class `DictItem` for further information. */
library class DictItem_ extends @py_dict_item {
/** Gets a parent of this dict_item */
DictItemList getParent() { py_dict_items(this, _, result, _) }
/** Gets a textual representation of this element. */
@@ -1456,6 +1663,7 @@ library class DictItem_ extends @py_dict_item {
/** INTERNAL: See the class `DictItemList` for further information. */
library class DictItemList_ extends @py_dict_item_list {
/** Gets a parent of this dict_item list */
DictItemListParent getParent() { py_dict_item_lists(this, result) }
/** Gets an item of this dict_item list */
@@ -1482,6 +1690,7 @@ library class Expr_ extends @py_expr {
/** Whether the parenthesised property of this expression is true. */
predicate isParenthesised() { py_bools(this, 1) }
/** Gets a parent of this expression */
ExprParent getParent() { py_exprs(this, _, result, _) }
/** Gets a textual representation of this element. */
@@ -1490,6 +1699,7 @@ library class Expr_ extends @py_expr {
/** INTERNAL: See the class `ExprContext` for further information. */
library class ExprContext_ extends @py_expr_context {
/** Gets a parent of this expression context */
ExprContextParent getParent() { py_expr_contexts(this, _, result) }
/** Gets a textual representation of this element. */
@@ -1504,6 +1714,7 @@ library class ExprContextParent_ extends @py_expr_context_parent {
/** INTERNAL: See the class `ExprList` for further information. */
library class ExprList_ extends @py_expr_list {
/** Gets a parent of this expression list */
ExprListParent getParent() { py_expr_lists(this, result, _) }
/** Gets an item of this expression list */
@@ -1556,6 +1767,7 @@ library class LocationParent_ extends @py_location_parent {
/** INTERNAL: See the class `Operator` for further information. */
library class Operator_ extends @py_operator {
/** Gets a parent of this operator */
BinaryExpr getParent() { py_operators(this, _, result) }
/** Gets a textual representation of this element. */
@@ -1568,6 +1780,48 @@ library class Parameter_ extends @py_parameter {
string toString() { result = "Parameter" }
}
/** INTERNAL: See the class `Pattern` for further information. */
library class Pattern_ extends @py_pattern {
/** Gets the location of this pattern. */
Location getLocation() { py_locations(result, this) }
/** Whether the parenthesised property of this pattern is true. */
predicate isParenthesised() { py_bools(this, 1) }
/** Gets a parent of this pattern */
PatternParent getParent() { py_patterns(this, _, result, _) }
/** Gets a textual representation of this element. */
string toString() { result = "Pattern" }
}
/** INTERNAL: See the class `PatternList` for further information. */
library class PatternList_ extends @py_pattern_list {
/** Gets a parent of this pattern list */
PatternListParent getParent() { py_pattern_lists(this, result, _) }
/** Gets an item of this pattern list */
Pattern getAnItem() { py_patterns(result, _, this, _) }
/** Gets the nth item of this pattern list */
Pattern getItem(int index) { py_patterns(result, _, this, index) }
/** Gets a textual representation of this element. */
string toString() { result = "PatternList" }
}
/** INTERNAL: See the class `PatternListParent` for further information. */
library class PatternListParent_ extends @py_pattern_list_parent {
/** Gets a textual representation of this element. */
string toString() { result = "PatternListParent" }
}
/** INTERNAL: See the class `PatternParent` for further information. */
library class PatternParent_ extends @py_pattern_parent {
/** Gets a textual representation of this element. */
string toString() { result = "PatternParent" }
}
/** INTERNAL: See the class `Scope` for further information. */
library class Scope_ extends @py_scope {
/** Gets a textual representation of this element. */
@@ -1579,6 +1833,7 @@ library class Stmt_ extends @py_stmt {
/** Gets the location of this statement. */
Location getLocation() { py_locations(result, this) }
/** Gets a parent of this statement */
StmtList getParent() { py_stmts(this, _, result, _) }
/** Gets a textual representation of this element. */
@@ -1587,6 +1842,7 @@ library class Stmt_ extends @py_stmt {
/** INTERNAL: See the class `StmtList` for further information. */
library class StmtList_ extends @py_stmt_list {
/** Gets a parent of this statement list */
StmtListParent getParent() { py_stmt_lists(this, result, _) }
/** Gets an item of this statement list */
@@ -1607,6 +1863,7 @@ library class StmtListParent_ extends @py_stmt_list_parent {
/** INTERNAL: See the class `StringList` for further information. */
library class StringList_ extends @py_str_list {
/** Gets a parent of this string list */
StrListParent getParent() { py_str_lists(this, result) }
/** Gets an item of this string list */
@@ -1633,6 +1890,7 @@ library class StrParent_ extends @py_str_parent {
/** INTERNAL: See the class `Unaryop` for further information. */
library class Unaryop_ extends @py_unaryop {
/** Gets a parent of this unary operation */
UnaryExpr getParent() { py_unaryops(this, _, result) }
/** Gets a textual representation of this element. */

View File

@@ -40,7 +40,7 @@ class Comment extends @py_comment {
private predicate comment_block_part(Comment start, Comment part, int i) {
not exists(Comment prev | prev.getFollowing() = part) and
exists(Comment following | part.getFollowing() = following) and
exists(part.getFollowing()) and
start = part and
i = 1
or

View File

@@ -514,7 +514,7 @@ class ComparisonControlBlock extends ConditionBlock {
Comparison getTest() { this.getLastNode() = result }
/** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */
/** Whether this conditional guard implies that, in block `b`, the result of `that` is `thatIsTrue` */
predicate impliesThat(BasicBlock b, Comparison that, boolean thatIsTrue) {
exists(boolean controlSense |
this.controls(b, controlSense) and

View File

@@ -326,9 +326,47 @@ module CodeExecution {
}
}
/**
* A data-flow node that constructs an SQL statement.
* Often, it is worthy of an alert if an SQL statement is constructed such that
* executing it would be a security risk.
*
* If it is important that the SQL statement is indeed executed, then use `SQLExecution`.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `SqlConstruction::Range` instead.
*/
class SqlConstruction extends DataFlow::Node {
SqlConstruction::Range range;
SqlConstruction() { this = range }
/** Gets the argument that specifies the SQL statements to be constructed. */
DataFlow::Node getSql() { result = range.getSql() }
}
/** Provides a class for modeling new SQL execution APIs. */
module SqlConstruction {
/**
* A data-flow node that constructs an SQL statement.
* Often, it is worthy of an alert if an SQL statement is constructed such that
* executing it would be a security risk.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `SqlExecution` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the argument that specifies the SQL statements to be constructed. */
abstract DataFlow::Node getSql();
}
}
/**
* A data-flow node that executes SQL statements.
*
* If the context of interest is such that merely constructing an SQL statement
* would be valuabe to report, then consider using `SqlConstruction`.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `SqlExecution::Range` instead.
*/
@@ -346,6 +384,9 @@ module SqlExecution {
/**
* A data-flow node that executes SQL statements.
*
* If the context of interest is such that merely constructing an SQL statement
* would be valuabe to report, then consider using `SqlConstruction`.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `SqlExecution` instead.
*/
@@ -771,6 +812,72 @@ module HTTP {
}
}
}
/** Provides classes for modeling HTTP clients. */
module Client {
/**
* A data-flow node that makes an outgoing HTTP request.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::Client::Request::Range` instead.
*/
class Request extends DataFlow::Node instanceof Request::Range {
/**
* Gets a data-flow node that contributes to the URL of the request.
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
*/
DataFlow::Node getAUrlPart() { result = super.getAUrlPart() }
/** Gets a string that identifies the framework used for this request. */
string getFramework() { result = super.getFramework() }
/**
* Holds if this request is made using a mode that disables SSL/TLS
* certificate validation, where `disablingNode` represents the point at
* which the validation was disabled, and `argumentOrigin` represents the origin
* of the argument that disabled the validation (which could be the same node as
* `disablingNode`).
*/
predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
super.disablesCertificateValidation(disablingNode, argumentOrigin)
}
}
/** Provides a class for modeling new HTTP requests. */
module Request {
/**
* A data-flow node that makes an outgoing HTTP request.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::Client::Request` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets a data-flow node that contributes to the URL of the request.
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
*/
abstract DataFlow::Node getAUrlPart();
/** Gets a string that identifies the framework used for this request. */
abstract string getFramework();
/**
* Holds if this request is made using a mode that disables SSL/TLS
* certificate validation, where `disablingNode` represents the point at
* which the validation was disabled, and `argumentOrigin` represents the origin
* of the argument that disabled the validation (which could be the same node as
* `disablingNode`).
*/
abstract predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
);
}
}
// TODO: investigate whether we should treat responses to client requests as
// remote-flow-sources in general.
}
}
/**

View File

@@ -718,6 +718,12 @@ class FormattedValue extends FormattedValue_ {
}
}
/** A guard in a case statement */
class Guard extends Guard_ {
/* syntax: if Expr */
override Expr getASubExpression() { result = this.getTest() }
}
/* Expression Contexts */
/** A context in which an expression used */
class ExprContext extends ExprContext_ { }

View File

@@ -6,13 +6,18 @@
// `docs/codeql/support/reusables/frameworks.rst`
private import semmle.python.frameworks.Aioch
private import semmle.python.frameworks.Aiohttp
private import semmle.python.frameworks.Aiomysql
private import semmle.python.frameworks.Aiopg
private import semmle.python.frameworks.Asyncpg
private import semmle.python.frameworks.ClickhouseDriver
private import semmle.python.frameworks.Cryptodome
private import semmle.python.frameworks.Cryptography
private import semmle.python.frameworks.Dill
private import semmle.python.frameworks.Django
private import semmle.python.frameworks.Fabric
private import semmle.python.frameworks.FastApi
private import semmle.python.frameworks.Flask
private import semmle.python.frameworks.FlaskAdmin
private import semmle.python.frameworks.FlaskSqlAlchemy
private import semmle.python.frameworks.Idna
private import semmle.python.frameworks.Invoke
@@ -23,12 +28,17 @@ private import semmle.python.frameworks.Mysql
private import semmle.python.frameworks.MySQLdb
private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.Pydantic
private import semmle.python.frameworks.PyMySQL
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.Simplejson
private import semmle.python.frameworks.SqlAlchemy
private import semmle.python.frameworks.Starlette
private import semmle.python.frameworks.Stdlib
private import semmle.python.frameworks.Toml
private import semmle.python.frameworks.Tornado
private import semmle.python.frameworks.Twisted
private import semmle.python.frameworks.Ujson

View File

@@ -18,7 +18,7 @@ class Function extends Function_, Scope, AstNode {
override Scope getScope() { result = this.getEnclosingScope() }
/** Whether this function is declared in a class */
predicate isMethod() { exists(Class cls | this.getEnclosingScope() = cls) }
predicate isMethod() { this.getEnclosingScope() instanceof Class }
/** Whether this is a special method, that is does its name have the form `__xxx__` (except `__init__`) */
predicate isSpecialMethod() {

View File

@@ -98,7 +98,7 @@ class LShift extends LShift_ {
override string getSpecialMethodName() { result = "__lshift__" }
}
/** A modulo (`%`) binary operator, which includes string formatting */
/** A modulo (`%`) binary operator, which includes string formatting */
class Mod extends Mod_ {
override string getSpecialMethodName() { result = "__mod__" }
}

View File

@@ -0,0 +1,118 @@
/**
* Wrapping generated AST classes: `Pattern_` and subclasses.
*/
import python
/** A pattern in a match statement */
class Pattern extends Pattern_, AstNode {
/** Gets the scope of this pattern */
override Scope getScope() { result = this.getCase().getScope() }
/** Gets the case statement containing this pattern */
Case getCase() { result.contains(this) }
override string toString() { result = "Pattern" }
/** Gets the module enclosing this pattern */
Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
/** Whether the parenthesized property of this expression is true. */
predicate isParenthesized() { Pattern_.super.isParenthesised() }
override Location getLocation() { result = Pattern_.super.getLocation() }
/** Gets an immediate (non-nested) sub-expression of this pattern */
Expr getASubExpression() { none() }
/** Gets an immediate (non-nested) sub-statement of this pattern */
Stmt getASubStatement() { none() }
/** Gets an immediate (non-nested) sub-pattern of this pattern */
Pattern getASubPattern() { none() }
override AstNode getAChildNode() {
result = this.getASubExpression()
or
result = this.getASubStatement()
or
result = this.getASubPattern()
}
}
/** An as-pattern in a match statement: `<subpattern> as alias` */
class MatchAsPattern extends MatchAsPattern_ {
override Pattern getASubPattern() { result = this.getPattern() }
override Expr getASubExpression() { result = this.getAlias() }
override Name getAlias() { result = super.getAlias() }
}
/** An or-pattern in a match statement: `(<pattern1>|<pattern2>)` */
class MatchOrPattern extends MatchOrPattern_ {
override Pattern getASubPattern() { result = this.getAPattern() }
}
/** A literal pattern in a match statement: `42` */
class MatchLiteralPattern extends MatchLiteralPattern_ {
override Expr getASubExpression() { result = this.getLiteral() }
}
/** A capture pattern in a match statement: `var` */
class MatchCapturePattern extends MatchCapturePattern_ {
/* syntax: varname */
override Expr getASubExpression() { result = this.getVariable() }
/** Gets the variable that is bound by this capture pattern */
override Name getVariable() { result = super.getVariable() }
}
/** A wildcard pattern in a match statement: `_` */
class MatchWildcardPattern extends MatchWildcardPattern_ { }
/** A value pattern in a match statement: `Http.OK` */
class MatchValuePattern extends MatchValuePattern_ {
override Expr getASubExpression() { result = this.getValue() }
}
/** A sequence pattern in a match statement `<p1>, <p2>` */
class MatchSequencePattern extends MatchSequencePattern_ {
override Pattern getASubPattern() { result = this.getAPattern() }
}
/** A star pattern in a match statement: `(..., *)` */
class MatchStarPattern extends MatchStarPattern_ {
override Pattern getASubPattern() { result = this.getTarget() }
}
/** A mapping pattern in a match statement: `{'a': var}` */
class MatchMappingPattern extends MatchMappingPattern_ {
override Pattern getASubPattern() { result = this.getAMapping() }
}
/** A double star pattern in a match statement: `{..., **}` */
class MatchDoubleStarPattern extends MatchDoubleStarPattern_ {
override Pattern getASubPattern() { result = this.getTarget() }
}
/** A key-value pattern inside a mapping pattern: `a: var` */
class MatchKeyValuePattern extends MatchKeyValuePattern_ {
override Pattern getASubPattern() { result = this.getKey() or result = this.getValue() }
}
/** A class pattern in a match statement: `Circle(radius = 3)` */
class MatchClassPattern extends MatchClassPattern_ {
override Expr getASubExpression() { result = this.getClassName() }
override Pattern getASubPattern() {
result = this.getAPositional() or result = this.getAKeyword()
}
}
/** A keyword pattern inside a class pattern: `radius = 3` */
class MatchKeywordPattern extends MatchKeywordPattern_ {
override Expr getASubExpression() { result = this.getAttribute() }
override Pattern getASubPattern() { result = this.getValue() }
}

View File

@@ -76,7 +76,7 @@ class PrintAstNode extends TPrintAstNode {
/**
* Gets a child of this node.
*/
final PrintAstNode getAChild() { result = getChild(_) }
final PrintAstNode getAChild() { result = this.getChild(_) }
/**
* Gets the parent of this node, if any.
@@ -94,7 +94,7 @@ class PrintAstNode extends TPrintAstNode {
*/
string getProperty(string key) {
key = "semmle.label" and
result = toString()
result = this.toString()
}
/**
@@ -103,7 +103,7 @@ class PrintAstNode extends TPrintAstNode {
* this.
*/
string getChildEdgeLabel(int childIndex) {
exists(getChild(childIndex)) and
exists(this.getChild(childIndex)) and
result = childIndex.toString()
}
}
@@ -157,13 +157,13 @@ class AstElementNode extends PrintAstNode, TElementNode {
override PrintAstNode getChild(int childIndex) {
exists(AstNode el | result.(AstElementNode).getAstNode() = el |
el = this.getChildNode(childIndex) and not el = getStmtList(_, _).getAnItem()
el = this.getChildNode(childIndex) and not el = this.getStmtList(_, _).getAnItem()
)
or
// displaying all `StmtList` after the other children.
exists(int offset | offset = 1 + max([0, any(int index | exists(this.getChildNode(index)))]) |
exists(int index | childIndex = index + offset |
result.(StmtListNode).getList() = getStmtList(index, _)
result.(StmtListNode).getList() = this.getStmtList(index, _)
)
)
}
@@ -299,7 +299,7 @@ class StmtListNode extends PrintAstNode, TStmtListNode {
private string getLabel() { this.getList() = any(AstElementNode node).getStmtList(_, result) }
override string toString() { result = "(StmtList) " + getLabel() }
override string toString() { result = "(StmtList) " + this.getLabel() }
override PrintAstNode getChild(int childIndex) {
exists(AstNode el | result.(AstElementNode).getAstNode() = el | el = list.getItem(childIndex))

Some files were not shown because too many files have changed in this diff Show More