Merge branch 'github:main' into crypto-test

This commit is contained in:
Nicolas Will
2025-04-30 16:35:26 +02:00
committed by GitHub
6768 changed files with 395014 additions and 163489 deletions

View File

@@ -1,3 +1,55 @@
## 4.0.6
No user-facing changes.
## 4.0.5
No user-facing changes.
## 4.0.4
### Minor Analysis Improvements
- Added the methods `getMinArguments` and `getMaxArguments` to the `Function` class. These return the minimum and maximum positional arguments that the given function accepts.
### Bug Fixes
- `MatchLiteralPattern`s such as `case None: ...` are now never pruned from the extracted source code. This fixes some situations where code was wrongly identified as unreachable.
## 4.0.3
No user-facing changes.
## 4.0.2
No user-facing changes.
## 4.0.1
### Bug Fixes
- Fixed a bug in the extractor where a comment inside a subscript could sometimes cause the AST to be missing nodes.
- Using the `break` and `continue` keywords outside of a loop, which is a syntax error but is accepted by our parser, would cause the control-flow construction to fail. This is now no longer the case.
## 4.0.0
### Breaking Changes
* Deleted the old deprecated TypeTracking library.
* Deleted the deprecated `classRef` predicate from the `FieldStorage` module, use `subclassRef` instead.
* Deleted a lot of deprecated modules and predicates from `Stdlib.qll`, use API-graphs directly instead.
### Minor Analysis Improvements
* Additional data flow models for the builtin functions `map`, `filter`, `zip`, and `enumerate` have been added.
## 3.1.1
### Minor Analysis Improvements
* The sensitive data library has been improved so that `snake_case` style variable names are recognized more reliably. This may result in more sensitive data being identified, and more results from queries that use the sensitive data library.
- Additional taint steps through methods of `lxml.etree.Element` and `lxml.etree.ElementTree` objects from the `lxml` PyPI package have been modeled.
## 3.1.0
### New Features

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
- Additional taint steps through methods of `lxml.etree.Element` and `lxml.etree.ElementTree` objects from the `lxml` PyPI package have been modeled.

View File

@@ -0,0 +1,6 @@
## 3.1.1
### Minor Analysis Improvements
* The sensitive data library has been improved so that `snake_case` style variable names are recognized more reliably. This may result in more sensitive data being identified, and more results from queries that use the sensitive data library.
- Additional taint steps through methods of `lxml.etree.Element` and `lxml.etree.ElementTree` objects from the `lxml` PyPI package have been modeled.

View File

@@ -0,0 +1,11 @@
## 4.0.0
### Breaking Changes
* Deleted the old deprecated TypeTracking library.
* Deleted the deprecated `classRef` predicate from the `FieldStorage` module, use `subclassRef` instead.
* Deleted a lot of deprecated modules and predicates from `Stdlib.qll`, use API-graphs directly instead.
### Minor Analysis Improvements
* Additional data flow models for the builtin functions `map`, `filter`, `zip`, and `enumerate` have been added.

View File

@@ -0,0 +1,6 @@
## 4.0.1
### Bug Fixes
- Fixed a bug in the extractor where a comment inside a subscript could sometimes cause the AST to be missing nodes.
- Using the `break` and `continue` keywords outside of a loop, which is a syntax error but is accepted by our parser, would cause the control-flow construction to fail. This is now no longer the case.

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
## 4.0.4
### Minor Analysis Improvements
- Added the methods `getMinArguments` and `getMaxArguments` to the `Function` class. These return the minimum and maximum positional arguments that the given function accepts.
### Bug Fixes
- `MatchLiteralPattern`s such as `case None: ...` are now never pruned from the extracted source code. This fixes some situations where code was wrongly identified as unreachable.

View File

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

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 3.1.0
lastReleaseVersion: 4.0.6

View File

@@ -1,5 +1,5 @@
name: codeql/python-all
version: 3.1.1-dev
version: 4.0.7-dev
groups: python
dbscheme: semmlecode.python.dbscheme
extractor: python

View File

@@ -181,7 +181,7 @@ module Path {
}
}
/** A data-flow node that checks that a path is safe to access. */
/** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */
class SafeAccessCheck extends DataFlow::ExprNode {
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
}
@@ -192,7 +192,7 @@ module Path {
/** Provides a class for modeling new path safety checks. */
module SafeAccessCheck {
/** A data-flow node that checks that a path is safe to access. */
/** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */
abstract class Range extends DataFlow::GuardNode {
/** Holds if this guard validates `node` upon evaluating to `branch`. */
abstract predicate checks(ControlFlowNode node, boolean branch);

View File

@@ -746,6 +746,24 @@ class Guard extends Guard_ {
override Expr getASubExpression() { result = this.getTest() }
}
/** An annotation, such as the `int` part of `x: int` */
class Annotation extends Expr {
Annotation() {
this = any(AnnAssign a).getAnnotation()
or
exists(Arguments args |
this in [
args.getAnAnnotation(),
args.getAKwAnnotation(),
args.getKwargannotation(),
args.getVarargannotation()
]
)
or
this = any(FunctionExpr f).getReturns()
}
}
/* Expression Contexts */
/** A context in which an expression used */
class ExprContext extends ExprContext_ { }

View File

@@ -163,6 +163,24 @@ class Function extends Function_, Scope, AstNode {
ret.getValue() = result.getNode()
)
}
/** Gets the minimum number of positional arguments that can be correctly passed to this function. */
int getMinPositionalArguments() {
result = count(this.getAnArg()) - count(this.getDefinition().getArgs().getADefault())
}
/**
* Gets the maximum number of positional arguments that can be correctly passed to this function.
*
* If the function has a `*vararg` parameter, there is no upper limit on the number of positional
* arguments that can be passed to the function. In this case, this method returns a very large
* number (currently `INT_MAX`, 2147483647, but this may change in the future).
*/
int getMaxPositionalArguments() {
if exists(this.getVararg())
then result = 2147483647 // INT_MAX
else result = count(this.getAnArg())
}
}
/** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */

View File

@@ -1,70 +0,0 @@
/**
* DEPRECATED: Use `semmle.python.dataflow.new.TypeTracking` instead.
*
* This file acts as a wrapper for `internal.TypeTracker`, exposing some of the functionality with
* names that are more appropriate for Python.
*/
private import python
private import internal.TypeTracker as Internal
private import internal.TypeTrackerSpecific as InternalSpecific
/** A string that may appear as the name of an attribute or access path. */
deprecated class AttributeName = InternalSpecific::TypeTrackerContent;
/** An attribute name, or the empty string (representing no attribute). */
deprecated class OptionalAttributeName = InternalSpecific::OptionalTypeTrackerContent;
/**
* DEPRECATED: Use `semmle.python.dataflow.new.TypeTracking` instead.
*
* The summary of the steps needed to track a value to a given dataflow node.
*
* This can be used to track objects that implement a certain API in order to
* recognize calls to that API. Note that type-tracking does not by itself provide a
* source/sink relation, that is, it may determine that a node has a given type,
* but it won't determine where that type came from.
*
* It is recommended that all uses of this type are written in the following form,
* for tracking some type `myType`:
* ```ql
* DataFlow::TypeTrackingNode myType(DataFlow::TypeTracker t) {
* t.start() and
* result = < source of myType >
* or
* exists (DataFlow::TypeTracker t2 |
* result = myType(t2).track(t2, t)
* )
* }
*
* DataFlow::LocalSourceNode myType() { myType(DataFlow::TypeTracker::end()) }
* ```
*
* Instead of `result = myType(t2).track(t2, t)`, you can also use the equivalent
* `t = t2.step(myType(t2), result)`. If you additionally want to track individual
* intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`.
*/
deprecated class TypeTracker extends Internal::TypeTracker {
/**
* Holds if this is the starting point of type tracking, and the value starts in the attribute named `attrName`.
* The type tracking only ends after the attribute has been loaded.
*/
predicate startInAttr(string attrName) { this.startInContent(attrName) }
/**
* INTERNAL. DO NOT USE.
*
* Gets the attribute associated with this type tracker.
*/
string getAttr() { result = this.getContent() }
}
deprecated module TypeTracker = Internal::TypeTracker;
deprecated class StepSummary = Internal::StepSummary;
deprecated module StepSummary = Internal::StepSummary;
deprecated class TypeBackTracker = Internal::TypeBackTracker;
deprecated module TypeBackTracker = Internal::TypeBackTracker;

View File

@@ -4,7 +4,8 @@
*/
private import internal.TypeTrackingImpl as Impl
import Impl::Shared::TypeTracking<Impl::TypeTrackingInput>
private import semmle.python.Files
import Impl::Shared::TypeTracking<Location, Impl::TypeTrackingInput>
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
/**

View File

@@ -1,957 +0,0 @@
/** Step Summaries and Type Tracking */
private import TypeTrackerSpecific
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
cached
private module Cached {
/**
* A description of a step on an inter-procedural data flow path.
*/
cached
deprecated newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
deprecated StoreStep(TypeTrackerContent content) {
exists(DataFlowPublic::AttributeContent dfc | dfc.getAttribute() = content |
basicStoreStep(_, _, dfc)
)
} or
deprecated LoadStep(TypeTrackerContent content) {
exists(DataFlowPublic::AttributeContent dfc | dfc.getAttribute() = content |
basicLoadStep(_, _, dfc)
)
} or
deprecated LoadStoreStep(TypeTrackerContent load, TypeTrackerContent store) {
exists(DataFlowPublic::AttributeContent dfcLoad, DataFlowPublic::AttributeContent dfcStore |
dfcLoad.getAttribute() = load and dfcStore.getAttribute() = store
|
basicLoadStoreStep(_, _, dfcLoad, dfcStore)
)
} or
deprecated WithContent(ContentFilter filter) { basicWithContentStep(_, _, filter) } or
deprecated WithoutContent(ContentFilter filter) { basicWithoutContentStep(_, _, filter) } or
JumpStep()
cached
deprecated newtype TTypeTracker =
deprecated MkTypeTracker(Boolean hasCall, OptionalTypeTrackerContent content) {
content = noContent()
or
// Restrict `content` to those that might eventually match a load.
// We can't rely on `basicStoreStep` since `startInContent` might be used with
// a content that has no corresponding store.
exists(DataFlowPublic::AttributeContent loadContents |
(
basicLoadStep(_, _, loadContents)
or
basicLoadStoreStep(_, _, loadContents, _)
) and
compatibleContents(content, loadContents.getAttribute())
)
}
cached
deprecated newtype TTypeBackTracker =
deprecated MkTypeBackTracker(Boolean hasReturn, OptionalTypeTrackerContent content) {
content = noContent()
or
// As in MkTypeTracker, restrict `content` to those that might eventually match a store.
exists(DataFlowPublic::AttributeContent storeContent |
(
basicStoreStep(_, _, storeContent)
or
basicLoadStoreStep(_, _, _, storeContent)
) and
compatibleContents(storeContent.getAttribute(), content)
)
}
/** Gets a type tracker with no content and the call bit set to the given value. */
cached
deprecated TypeTracker noContentTypeTracker(boolean hasCall) {
result = MkTypeTracker(hasCall, noContent())
}
/** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */
cached
deprecated TypeTracker append(TypeTracker tt, StepSummary step) {
exists(Boolean hasCall, OptionalTypeTrackerContent currentContents |
tt = MkTypeTracker(hasCall, currentContents)
|
step = LevelStep() and result = tt
or
step = CallStep() and result = MkTypeTracker(true, currentContents)
or
step = ReturnStep() and hasCall = false and result = tt
or
step = JumpStep() and
result = MkTypeTracker(false, currentContents)
or
exists(ContentFilter filter | result = tt |
step = WithContent(filter) and
currentContents = filter.getAMatchingContent()
or
step = WithoutContent(filter) and
not currentContents = filter.getAMatchingContent()
)
)
or
exists(TypeTrackerContent storeContents, boolean hasCall |
exists(TypeTrackerContent loadContents |
step = LoadStep(pragma[only_bind_into](loadContents)) and
tt = MkTypeTracker(hasCall, storeContents) and
compatibleContents(storeContents, loadContents) and
result = noContentTypeTracker(hasCall)
)
or
step = StoreStep(pragma[only_bind_into](storeContents)) and
tt = noContentTypeTracker(hasCall) and
result = MkTypeTracker(hasCall, storeContents)
)
or
exists(
TypeTrackerContent currentContent, TypeTrackerContent store, TypeTrackerContent load,
boolean hasCall
|
step = LoadStoreStep(pragma[only_bind_into](load), pragma[only_bind_into](store)) and
compatibleContents(pragma[only_bind_into](currentContent), load) and
tt = MkTypeTracker(pragma[only_bind_into](hasCall), currentContent) and
result = MkTypeTracker(pragma[only_bind_out](hasCall), store)
)
}
pragma[nomagic]
deprecated private TypeBackTracker noContentTypeBackTracker(boolean hasReturn) {
result = MkTypeBackTracker(hasReturn, noContent())
}
/** Gets the summary resulting from prepending `step` to this type-tracking summary. */
cached
deprecated TypeBackTracker prepend(TypeBackTracker tbt, StepSummary step) {
exists(Boolean hasReturn, OptionalTypeTrackerContent content |
tbt = MkTypeBackTracker(hasReturn, content)
|
step = LevelStep() and result = tbt
or
step = CallStep() and hasReturn = false and result = tbt
or
step = ReturnStep() and result = MkTypeBackTracker(true, content)
or
step = JumpStep() and
result = MkTypeBackTracker(false, content)
or
exists(ContentFilter filter | result = tbt |
step = WithContent(filter) and
content = filter.getAMatchingContent()
or
step = WithoutContent(filter) and
not content = filter.getAMatchingContent()
)
)
or
exists(TypeTrackerContent loadContents, boolean hasReturn |
exists(TypeTrackerContent storeContents |
step = StoreStep(pragma[only_bind_into](storeContents)) and
tbt = MkTypeBackTracker(hasReturn, loadContents) and
compatibleContents(storeContents, loadContents) and
result = noContentTypeBackTracker(hasReturn)
)
or
step = LoadStep(pragma[only_bind_into](loadContents)) and
tbt = noContentTypeBackTracker(hasReturn) and
result = MkTypeBackTracker(hasReturn, loadContents)
)
or
exists(
TypeTrackerContent currentContent, TypeTrackerContent store, TypeTrackerContent load,
boolean hasCall
|
step = LoadStoreStep(pragma[only_bind_into](load), pragma[only_bind_into](store)) and
compatibleContents(store, pragma[only_bind_into](currentContent)) and
tbt = MkTypeBackTracker(pragma[only_bind_into](hasCall), currentContent) and
result = MkTypeBackTracker(pragma[only_bind_out](hasCall), load)
)
}
/**
* Gets the summary that corresponds to having taken a forwards
* heap and/or intra-procedural step from `nodeFrom` to `nodeTo`.
*
* Steps contained in this predicate should _not_ depend on the call graph.
*/
cached
deprecated predicate stepNoCall(
TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary
) {
exists(Node mid | nodeFrom.flowsTo(mid) and smallstepNoCall(mid, nodeTo, summary))
}
/**
* Gets the summary that corresponds to having taken a forwards
* inter-procedural step from `nodeFrom` to `nodeTo`.
*/
cached
deprecated predicate stepCall(
TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary
) {
exists(Node mid | nodeFrom.flowsTo(mid) and smallstepCall(mid, nodeTo, summary))
}
cached
deprecated predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
jumpStep(nodeFrom, nodeTo) and
summary = JumpStep()
or
levelStepNoCall(nodeFrom, nodeTo) and
summary = LevelStep()
or
exists(TypeTrackerContent content |
flowsToStoreStep(nodeFrom, nodeTo, content) and
summary = StoreStep(content)
or
exists(DataFlowPublic::AttributeContent dfc | dfc.getAttribute() = content |
basicLoadStep(nodeFrom, nodeTo, dfc)
) and
summary = LoadStep(content)
)
or
exists(TypeTrackerContent loadContent, TypeTrackerContent storeContent |
flowsToLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent) and
summary = LoadStoreStep(loadContent, storeContent)
)
or
exists(ContentFilter filter |
basicWithContentStep(nodeFrom, nodeTo, filter) and
summary = WithContent(filter)
or
basicWithoutContentStep(nodeFrom, nodeTo, filter) and
summary = WithoutContent(filter)
)
}
cached
deprecated predicate smallstepCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
callStep(nodeFrom, nodeTo) and summary = CallStep()
or
returnStep(nodeFrom, nodeTo) and
summary = ReturnStep()
or
levelStepCall(nodeFrom, nodeTo) and
summary = LevelStep()
}
}
private import Cached
deprecated private predicate step(
TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary
) {
stepNoCall(nodeFrom, nodeTo, summary)
or
stepCall(nodeFrom, nodeTo, summary)
}
pragma[nomagic]
deprecated private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) {
step(nodeFrom, _, summary)
}
deprecated private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
smallstepNoCall(nodeFrom, nodeTo, summary)
or
smallstepCall(nodeFrom, nodeTo, summary)
}
pragma[nomagic]
deprecated private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
smallstep(nodeFrom, _, summary)
}
/**
* Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`.
*
* Note that `nodeTo` will always be a local source node that flows to the place where the content
* is written in `basicStoreStep`. This may lead to the flow of information going "back in time"
* from the point of view of the execution of the program.
*
* For instance, if we interpret attribute writes in Python as writing to content with the same
* name as the attribute and consider the following snippet
*
* ```python
* def foo(y):
* x = Foo()
* bar(x)
* x.attr = y
* baz(x)
*
* def bar(x):
* z = x.attr
* ```
* for the attribute write `x.attr = y`, we will have `content` being the literal string `"attr"`,
* `nodeFrom` will be `y`, and `nodeTo` will be the object `Foo()` created on the first line of the
* function. This means we will track the fact that `x.attr` can have the type of `y` into the
* assignment to `z` inside `bar`, even though this attribute write happens _after_ `bar` is called.
*/
deprecated private predicate flowsToStoreStep(
Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent content
) {
exists(Node obj |
nodeTo.flowsTo(obj) and
exists(DataFlowPublic::AttributeContent dfc | dfc.getAttribute() = content |
basicStoreStep(nodeFrom, obj, dfc)
)
)
}
/**
* Holds if `loadContent` is loaded from `nodeFrom` and written to `storeContent` of `nodeTo`.
*/
deprecated private predicate flowsToLoadStoreStep(
Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent loadContent,
TypeTrackerContent storeContent
) {
exists(Node obj |
nodeTo.flowsTo(obj) and
exists(DataFlowPublic::AttributeContent loadDfc, DataFlowPublic::AttributeContent storeDfc |
loadDfc.getAttribute() = loadContent and storeDfc.getAttribute() = storeContent
|
basicLoadStoreStep(nodeFrom, obj, loadDfc, storeDfc)
)
)
}
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
* A description of a step on an inter-procedural data flow path.
*/
deprecated class StepSummary extends TStepSummary {
/** Gets a textual representation of this step summary. */
string toString() {
this instanceof LevelStep and result = "level"
or
this instanceof CallStep and result = "call"
or
this instanceof ReturnStep and result = "return"
or
exists(TypeTrackerContent content | this = StoreStep(content) | result = "store " + content)
or
exists(TypeTrackerContent content | this = LoadStep(content) | result = "load " + content)
or
exists(TypeTrackerContent load, TypeTrackerContent store |
this = LoadStoreStep(load, store) and
result = "load-store " + load + " -> " + store
)
or
this instanceof JumpStep and result = "jump"
}
}
/** Provides predicates for updating step summaries (`StepSummary`s). */
deprecated module StepSummary {
predicate append = Cached::append/2;
/**
* Gets the summary that corresponds to having taken a forwards
* inter-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate stepCall = Cached::stepCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* intra-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate stepNoCall = Cached::stepNoCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
*/
predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
stepNoCall(nodeFrom, nodeTo, summary)
or
stepCall(nodeFrom, nodeTo, summary)
}
/**
* Gets the summary that corresponds to having taken a forwards
* inter-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate smallstepNoCall = Cached::smallstepNoCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* intra-procedural step from `nodeFrom` to `nodeTo`.
*
* This predicate should normally not be used; consider using `step`
* instead.
*/
predicate smallstepCall = Cached::smallstepCall/3;
/**
* Gets the summary that corresponds to having taken a forwards
* local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
*
* Unlike `StepSummary::step`, this predicate does not compress
* type-preserving steps.
*/
predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
smallstepNoCall(nodeFrom, nodeTo, summary)
or
smallstepCall(nodeFrom, nodeTo, summary)
}
/** Gets the step summary for a level step. */
StepSummary levelStep() { result = LevelStep() }
/** Gets the step summary for a call step. */
StepSummary callStep() { result = CallStep() }
/** Gets the step summary for a return step. */
StepSummary returnStep() { result = ReturnStep() }
/** Gets the step summary for storing into `content`. */
StepSummary storeStep(TypeTrackerContent content) { result = StoreStep(content) }
/** Gets the step summary for loading from `content`. */
StepSummary loadStep(TypeTrackerContent content) { result = LoadStep(content) }
/** Gets the step summary for loading from `load` and then storing into `store`. */
StepSummary loadStoreStep(TypeTrackerContent load, TypeTrackerContent store) {
result = LoadStoreStep(load, store)
}
/** Gets the step summary for a step that only permits contents matched by `filter`. */
StepSummary withContent(ContentFilter filter) { result = WithContent(filter) }
/** Gets the step summary for a step that blocks contents matched by `filter`. */
StepSummary withoutContent(ContentFilter filter) { result = WithoutContent(filter) }
/** Gets the step summary for a jump step. */
StepSummary jumpStep() { result = JumpStep() }
}
/**
* DEPRECATED: Use `semmle.python.dataflow.new.TypeTracking` instead.
*
* A summary of the steps needed to track a value to a given dataflow node.
*
* This can be used to track objects that implement a certain API in order to
* recognize calls to that API. Note that type-tracking does not by itself provide a
* source/sink relation, that is, it may determine that a node has a given type,
* but it won't determine where that type came from.
*
* It is recommended that all uses of this type are written in the following form,
* for tracking some type `myType`:
* ```ql
* DataFlow::TypeTrackingNode myType(DataFlow::TypeTracker t) {
* t.start() and
* result = < source of myType >
* or
* exists (DataFlow::TypeTracker t2 |
* result = myType(t2).track(t2, t)
* )
* }
*
* DataFlow::Node myType() { myType(DataFlow::TypeTracker::end()).flowsTo(result) }
* ```
*
* Instead of `result = myType(t2).track(t2, t)`, you can also use the equivalent
* `t = t2.step(myType(t2), result)`. If you additionally want to track individual
* intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`.
*/
deprecated class TypeTracker extends TTypeTracker {
Boolean hasCall;
OptionalTypeTrackerContent content;
TypeTracker() { this = MkTypeTracker(hasCall, content) }
/** Gets the summary resulting from appending `step` to this type-tracking summary. */
TypeTracker append(StepSummary step) { result = append(this, step) }
/** Gets a textual representation of this summary. */
string toString() {
exists(string withCall, string withContent |
(if hasCall = true then withCall = "with" else withCall = "without") and
(
if content != noContent()
then withContent = " with content " + content
else withContent = ""
) and
result = "type tracker " + withCall + " call steps" + withContent
)
}
/**
* Holds if this is the starting point of type tracking.
*/
predicate start() { hasCall = false and content = noContent() }
/**
* Holds if this is the starting point of type tracking, and the value starts in the content named `contentName`.
* The type tracking only ends after the content has been loaded.
*/
predicate startInContent(TypeTrackerContent contentName) {
hasCall = false and content = contentName
}
/**
* Holds if this is the starting point of type tracking
* when tracking a parameter into a call, but not out of it.
*/
predicate call() { hasCall = true and content = noContent() }
/**
* Holds if this is the end point of type tracking.
*/
predicate end() { content = noContent() }
/**
* INTERNAL. DO NOT USE.
*
* Holds if this type has been tracked into a call.
*/
boolean hasCall() { result = hasCall }
/**
* INTERNAL. DO NOT USE.
*
* Gets the content associated with this type tracker.
*/
OptionalTypeTrackerContent getContent() { result = content }
/**
* Gets a type tracker that starts where this one has left off to allow continued
* tracking.
*
* This predicate is only defined if the type is not associated to a piece of content.
*/
TypeTracker continue() { content = noContent() and result = this }
/**
* Gets the summary that corresponds to having taken a forwards
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
*/
bindingset[nodeFrom, this]
pragma[inline_late]
pragma[noopt]
TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
exists(StepSummary summary |
stepProj(nodeFrom, summary) and
result = this.append(summary) and
step(nodeFrom, nodeTo, summary)
)
}
bindingset[nodeFrom, this]
pragma[inline_late]
pragma[noopt]
private TypeTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
smallstepProj(nodeFrom, summary) and
result = this.append(summary) and
smallstep(nodeFrom, nodeTo, summary)
)
}
/**
* Gets the summary that corresponds to having taken a forwards
* local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
*
* Unlike `TypeTracker::step`, this predicate exposes all edges
* in the flow graph, and not just the edges between `Node`s.
* It may therefore be less performant.
*
* Type tracking predicates using small steps typically take the following form:
* ```ql
* DataFlow::Node myType(DataFlow::TypeTracker t) {
* t.start() and
* result = < source of myType >
* or
* exists (DataFlow::TypeTracker t2 |
* t = t2.smallstep(myType(t2), result)
* )
* }
*
* DataFlow::Node myType() {
* result = myType(DataFlow::TypeTracker::end())
* }
* ```
*/
pragma[inline]
TypeTracker smallstep(Node nodeFrom, Node nodeTo) {
result = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo)
or
simpleLocalFlowStep(nodeFrom, nodeTo) and
result = this
}
}
/** Provides predicates for implementing custom `TypeTracker`s. */
deprecated module TypeTracker {
/**
* Gets a valid end point of type tracking.
*/
TypeTracker end() { result.end() }
/**
* INTERNAL USE ONLY.
*
* Gets a valid end point of type tracking with the call bit set to the given value.
*/
predicate end = Cached::noContentTypeTracker/1;
}
pragma[nomagic]
deprecated private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) {
step(_, nodeTo, summary)
}
deprecated private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) {
smallstep(_, nodeTo, summary)
}
/**
* DEPRECATED: Use `semmle.python.dataflow.new.TypeTracking` instead.
*
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
*
* This can for example be used to track callbacks that are passed to a certain API,
* so we can model specific parameters of that callback as having a certain type.
*
* Note that type back-tracking does not provide a source/sink relation, that is,
* it may determine that a node will be used in an API call somewhere, but it won't
* determine exactly where that use was, or the path that led to the use.
*
* It is recommended that all uses of this type are written in the following form,
* for back-tracking some callback type `myCallback`:
*
* ```ql
* DataFlow::TypeTrackingNode myCallback(DataFlow::TypeBackTracker t) {
* t.start() and
* result = (< some API call >).getArgument(< n >).getALocalSource()
* or
* exists (DataFlow::TypeBackTracker t2 |
* result = myCallback(t2).backtrack(t2, t)
* )
* }
*
* DataFlow::TypeTrackingNode myCallback() { result = myCallback(DataFlow::TypeBackTracker::end()) }
* ```
*
* Instead of `result = myCallback(t2).backtrack(t2, t)`, you can also use the equivalent
* `t2 = t.step(result, myCallback(t2))`. If you additionally want to track individual
* intra-procedural steps, use `t2 = t.smallstep(result, myCallback(t2))`.
*/
deprecated class TypeBackTracker extends TTypeBackTracker {
Boolean hasReturn;
OptionalTypeTrackerContent content;
TypeBackTracker() { this = MkTypeBackTracker(hasReturn, content) }
/** Gets the summary resulting from prepending `step` to this type-tracking summary. */
TypeBackTracker prepend(StepSummary step) { result = prepend(this, step) }
/** Gets a textual representation of this summary. */
string toString() {
exists(string withReturn, string withContent |
(if hasReturn = true then withReturn = "with" else withReturn = "without") and
(
if content != noContent()
then withContent = " with content " + content
else withContent = ""
) and
result = "type back-tracker " + withReturn + " return steps" + withContent
)
}
/**
* Holds if this is the starting point of type tracking.
*/
predicate start() { hasReturn = false and content = noContent() }
/**
* Holds if this is the end point of type tracking.
*/
predicate end() { content = noContent() }
/**
* INTERNAL. DO NOT USE.
*
* Holds if this type has been back-tracked into a call through return edge.
*/
boolean hasReturn() { result = hasReturn }
/**
* Gets a type tracker that starts where this one has left off to allow continued
* tracking.
*
* This predicate is only defined if the type has not been tracked into a piece of content.
*/
TypeBackTracker continue() { content = noContent() and result = this }
/**
* Gets the summary that corresponds to having taken a backwards
* heap and/or inter-procedural step from `nodeTo` to `nodeFrom`.
*/
bindingset[nodeTo, result]
pragma[inline_late]
pragma[noopt]
TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
exists(StepSummary summary |
backStepProj(nodeTo, summary) and
this = result.prepend(summary) and
step(nodeFrom, nodeTo, summary)
)
}
bindingset[nodeTo, result]
pragma[inline_late]
pragma[noopt]
private TypeBackTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
backSmallstepProj(nodeTo, summary) and
this = result.prepend(summary) and
smallstep(nodeFrom, nodeTo, summary)
)
}
/**
* Gets the summary that corresponds to having taken a backwards
* local, heap and/or inter-procedural step from `nodeTo` to `nodeFrom`.
*
* Unlike `TypeBackTracker::step`, this predicate exposes all edges
* in the flowgraph, and not just the edges between
* `TypeTrackingNode`s. It may therefore be less performant.
*
* Type tracking predicates using small steps typically take the following form:
* ```ql
* DataFlow::Node myType(DataFlow::TypeBackTracker t) {
* t.start() and
* result = < some API call >.getArgument(< n >)
* or
* exists (DataFlow::TypeBackTracker t2 |
* t = t2.smallstep(result, myType(t2))
* )
* }
*
* DataFlow::Node myType() {
* result = myType(DataFlow::TypeBackTracker::end())
* }
* ```
*/
pragma[inline]
TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) {
this = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo)
or
simpleLocalFlowStep(nodeFrom, nodeTo) and
this = result
}
/**
* Gets a forwards summary that is compatible with this backwards summary.
* That is, if this summary describes the steps needed to back-track a value
* from `sink` to `mid`, and the result is a valid summary of the steps needed
* to track a value from `source` to `mid`, then the value from `source` may
* also flow to `sink`.
*/
TypeTracker getACompatibleTypeTracker() {
exists(boolean hasCall, OptionalTypeTrackerContent c |
result = MkTypeTracker(hasCall, c) and
(
compatibleContents(c, content)
or
content = noContent() and c = content
)
|
hasCall = false
or
this.hasReturn() = false
)
}
}
/** Provides predicates for implementing custom `TypeBackTracker`s. */
deprecated module TypeBackTracker {
/**
* Gets a valid end point of type back-tracking.
*/
TypeBackTracker end() { result.end() }
}
/**
* INTERNAL: Do not use.
*
* Provides logic for constructing a call graph in mutual recursion with type tracking.
*
* When type tracking is used to construct a call graph, we cannot use the join-order
* from `stepInlineLate`, because `step` becomes a recursive call, which means that we
* will have a conjunct with 3 recursive calls: the call to `step`, the call to `stepProj`,
* and the recursive type tracking call itself. The solution is to split the three-way
* non-linear recursion into two non-linear predicates: one that first joins with the
* projected `stepCall` relation, followed by a predicate that joins with the full
* `stepCall` relation (`stepNoCall` not being recursive, can be join-ordered in the
* same way as in `stepInlineLate`).
*/
deprecated module CallGraphConstruction {
/** The input to call graph construction. */
signature module InputSig {
/** A state to track during type tracking. */
class State;
/** Holds if type tracking should start at `start` in state `state`. */
deprecated predicate start(Node start, State state);
/**
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
* which _does not_ depend on the call graph.
*
* Implementing this predicate using `StepSummary::[small]stepNoCall` yields
* standard type tracking.
*/
deprecated predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary);
/**
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
* which _does_ depend on the call graph.
*
* Implementing this predicate using `StepSummary::[small]stepCall` yields
* standard type tracking.
*/
deprecated predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary);
/** A projection of an element from the state space. */
class StateProj;
/** Gets the projection of `state`. */
StateProj stateProj(State state);
/** Holds if type tracking should stop at `n` when we are tracking projected state `stateProj`. */
deprecated predicate filter(Node n, StateProj stateProj);
}
/** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> {
pragma[nomagic]
deprecated private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) {
Input::stepNoCall(nodeFrom, _, summary)
}
pragma[nomagic]
deprecated private predicate stepCallProj(Node nodeFrom, StepSummary summary) {
Input::stepCall(nodeFrom, _, summary)
}
bindingset[nodeFrom, t]
pragma[inline_late]
pragma[noopt]
deprecated private TypeTracker stepNoCallInlineLate(
TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
) {
exists(StepSummary summary |
stepNoCallProj(nodeFrom, summary) and
result = t.append(summary) and
Input::stepNoCall(nodeFrom, nodeTo, summary)
)
}
bindingset[state]
pragma[inline_late]
private Input::StateProj stateProjInlineLate(Input::State state) {
result = Input::stateProj(state)
}
pragma[nomagic]
deprecated private Node track(Input::State state, TypeTracker t) {
t.start() and Input::start(result, state)
or
exists(Input::StateProj stateProj |
stateProj = stateProjInlineLate(state) and
not Input::filter(result, stateProj)
|
exists(TypeTracker t2 | t = stepNoCallInlineLate(t2, track(state, t2), result))
or
exists(StepSummary summary |
// non-linear recursion
Input::stepCall(trackCall(state, t, summary), result, summary)
)
)
}
bindingset[t, summary]
pragma[inline_late]
deprecated private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) {
result = t.append(summary)
}
pragma[nomagic]
deprecated private Node trackCall(Input::State state, TypeTracker t, StepSummary summary) {
exists(TypeTracker t2 |
// non-linear recursion
result = track(state, t2) and
stepCallProj(result, summary) and
t = appendInlineLate(t2, summary)
)
}
/** Gets a node that can be reached from _some_ start node in state `state`. */
pragma[nomagic]
deprecated Node track(Input::State state) { result = track(state, TypeTracker::end()) }
}
/** A simple version of `CallGraphConstruction` that uses standard type tracking. */
module Simple {
/** The input to call graph construction. */
signature module InputSig {
/** A state to track during type tracking. */
class State;
/** Holds if type tracking should start at `start` in state `state`. */
deprecated predicate start(Node start, State state);
/** Holds if type tracking should stop at `n`. */
deprecated predicate filter(Node n);
}
/** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> {
deprecated private module I implements CallGraphConstruction::InputSig {
private import codeql.util.Unit
class State = Input::State;
predicate start(Node start, State state) { Input::start(start, state) }
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
StepSummary::stepNoCall(nodeFrom, nodeTo, summary)
}
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
StepSummary::stepCall(nodeFrom, nodeTo, summary)
}
class StateProj = Unit;
Unit stateProj(State state) { exists(state) and exists(result) }
predicate filter(Node n, Unit u) {
Input::filter(n) and
exists(u)
}
}
deprecated import CallGraphConstruction::Make<I>
}
}
}

View File

@@ -1,102 +0,0 @@
/**
* Provides Python-specific definitions for use in the type tracker library.
*/
private import python
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
private import TypeTrackingImpl as TypeTrackingImpl
deprecated class Node = DataFlowPublic::Node;
deprecated class TypeTrackingNode = DataFlowPublic::TypeTrackingNode;
/** A content name for use by type trackers, or the empty string. */
deprecated class OptionalTypeTrackerContent extends string {
OptionalTypeTrackerContent() {
this = ""
or
this = any(DataFlowPublic::AttributeContent dfc).getAttribute()
}
}
/** A content name for use by type trackers. */
deprecated class TypeTrackerContent extends OptionalTypeTrackerContent {
TypeTrackerContent() { this != "" }
}
/** Gets the content string representing no value. */
deprecated OptionalTypeTrackerContent noContent() { result = "" }
/**
* A label to use for `WithContent` and `WithoutContent` steps, restricting
* which `ContentSet` may pass through. Not currently used in Python.
*/
deprecated class ContentFilter extends Unit {
TypeTrackerContent getAMatchingContent() { none() }
}
pragma[inline]
deprecated predicate compatibleContents(
TypeTrackerContent storeContent, TypeTrackerContent loadContent
) {
storeContent = loadContent
}
deprecated predicate simpleLocalFlowStep =
TypeTrackingImpl::TypeTrackingInput::simpleLocalSmallStep/2;
deprecated predicate jumpStep = TypeTrackingImpl::TypeTrackingInput::jumpStep/2;
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
deprecated predicate levelStepCall(Node nodeFrom, Node nodeTo) { none() }
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
deprecated predicate levelStepNoCall = TypeTrackingImpl::TypeTrackingInput::levelStepNoCall/2;
/**
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
*
* Flow into summarized library methods is not included, as that will lead to negative
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
deprecated predicate callStep = TypeTrackingImpl::TypeTrackingInput::callStep/2;
/** Holds if `nodeFrom` steps to `nodeTo` by being returned from a call. */
deprecated predicate returnStep = TypeTrackingImpl::TypeTrackingInput::returnStep/2;
/**
* Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`.
*/
deprecated predicate basicStoreStep = TypeTrackingImpl::TypeTrackingInput::storeStep/3;
/**
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
*/
deprecated predicate basicLoadStep = TypeTrackingImpl::TypeTrackingInput::loadStep/3;
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
deprecated predicate basicLoadStoreStep = TypeTrackingImpl::TypeTrackingInput::loadStoreStep/4;
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
deprecated predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
none()
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
deprecated predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
none()
}
/**
* A utility class that is equivalent to `boolean` but does not require type joining.
*/
deprecated class Boolean extends boolean {
Boolean() { this = true or this = false }
}

View File

@@ -106,7 +106,7 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
module TypeTrackingInput implements Shared::TypeTrackingInput {
module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
class Node = DataFlowPublic::Node;
class LocalSourceNode = DataFlowPublic::LocalSourceNode;
@@ -323,4 +323,4 @@ module TypeTrackingInput implements Shared::TypeTrackingInput {
predicate nonStandardFlowsTo(LocalSourceNode localSource, Node dst) { localSource.flowsTo(dst) }
}
import SharedImpl::TypeTracking<TypeTrackingInput>
import SharedImpl::TypeTracking<Location, TypeTrackingInput>

View File

@@ -1781,15 +1781,6 @@ module StdlibPrivate {
* See https://docs.python.org/3/library/cgi.html.
*/
module FieldStorage {
/**
* DEPRECATED: Use `subclassRef` predicate instead.
*
* Gets a reference to the `cgi.FieldStorage` class.
*/
deprecated API::Node classRef() {
result = API::moduleImport("cgi").getMember("FieldStorage")
}
/** Gets a reference to the `cgi.FieldStorage` class or any subclass. */
API::Node subclassRef() {
result = API::moduleImport("cgi").getMember("FieldStorage").getASubclass*()
@@ -1900,168 +1891,15 @@ module StdlibPrivate {
// ---------------------------------------------------------------------------
// BaseHTTPServer (Python 2 only)
// ---------------------------------------------------------------------------
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `BaseHttpServer` module.
*/
deprecated API::Node baseHttpServer() { result = API::moduleImport("BaseHTTPServer") }
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `BaseHttpServer` module.
*/
deprecated module BaseHttpServer {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `BaseHTTPServer.BaseHTTPRequestHandler` class (Python 2 only).
*/
deprecated module BaseHttpRequestHandler {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `BaseHttpServer.BaseHttpRequestHandler` class.
*/
deprecated API::Node classRef() {
result = baseHttpServer().getMember("BaseHTTPRequestHandler")
}
}
}
// ---------------------------------------------------------------------------
// SimpleHTTPServer (Python 2 only)
// ---------------------------------------------------------------------------
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `SimpleHttpServer` module.
*/
deprecated API::Node simpleHttpServer() { result = API::moduleImport("SimpleHTTPServer") }
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `SimpleHttpServer` module.
*/
deprecated module SimpleHttpServer {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `SimpleHTTPServer.SimpleHTTPRequestHandler` class (Python 2 only).
*/
deprecated module SimpleHttpRequestHandler {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `SimpleHttpServer.SimpleHttpRequestHandler` class.
*/
deprecated API::Node classRef() {
result = simpleHttpServer().getMember("SimpleHTTPRequestHandler")
}
}
}
// ---------------------------------------------------------------------------
// CGIHTTPServer (Python 2 only)
// ---------------------------------------------------------------------------
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `CGIHTTPServer` module.
*/
deprecated API::Node cgiHttpServer() { result = API::moduleImport("CGIHTTPServer") }
/** Provides models for the `CGIHTTPServer` module. */
deprecated module CgiHttpServer {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `CGIHTTPServer.CGIHTTPRequestHandler` class (Python 2 only).
*/
deprecated module CgiHttpRequestHandler {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `CGIHTTPServer.CgiHttpRequestHandler` class.
*/
deprecated API::Node classRef() {
result = cgiHttpServer().getMember("CGIHTTPRequestHandler")
}
}
}
// ---------------------------------------------------------------------------
// http (Python 3 only)
// ---------------------------------------------------------------------------
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `http` module.
*/
deprecated API::Node http() { result = API::moduleImport("http") }
/** Provides models for the `http` module. */
deprecated module StdlibHttp {
// -------------------------------------------------------------------------
// http.server
// -------------------------------------------------------------------------
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `http.server` module.
*/
deprecated API::Node server() { result = http().getMember("server") }
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `http.server` module
*/
deprecated module Server {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `http.server.BaseHTTPRequestHandler` class (Python 3 only).
*
* See https://docs.python.org/3.9/library/http.server.html#http.server.BaseHTTPRequestHandler.
*/
deprecated module BaseHttpRequestHandler {
/** Gets a reference to the `http.server.BaseHttpRequestHandler` class. */
deprecated API::Node classRef() { result = server().getMember("BaseHTTPRequestHandler") }
}
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `http.server.SimpleHTTPRequestHandler` class (Python 3 only).
*
* See https://docs.python.org/3.9/library/http.server.html#http.server.SimpleHTTPRequestHandler.
*/
deprecated module SimpleHttpRequestHandler {
/** Gets a reference to the `http.server.SimpleHttpRequestHandler` class. */
deprecated API::Node classRef() { result = server().getMember("SimpleHTTPRequestHandler") }
}
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Provides models for the `http.server.CGIHTTPRequestHandler` class (Python 3 only).
*
* See https://docs.python.org/3.9/library/http.server.html#http.server.CGIHTTPRequestHandler.
*/
deprecated module CgiHttpRequestHandler {
/**
* DEPRECATED: Use API-graphs directly instead.
*
* Gets a reference to the `http.server.CGIHTTPRequestHandler` class.
*/
deprecated API::Node classRef() { result = server().getMember("CGIHTTPRequestHandler") }
}
}
}
/**
* Provides models for the `BaseHTTPRequestHandler` class and subclasses.
*
@@ -4523,6 +4361,124 @@ module StdlibPrivate {
}
}
/** A flow summary for `map`. */
class MapSummary extends SummarizedCallable {
MapSummary() { this = "builtins.map" }
override DataFlow::CallCfgNode getACall() { result = API::builtin("map").getACall() }
override DataFlow::ArgumentNode getACallback() {
result = API::builtin("map").getAValueReachableFromSource()
}
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(int i | exists(any(Call c).getArg(i)) |
(
input = "Argument[" + (i + 1).toString() + "].ListElement"
or
input = "Argument[" + (i + 1).toString() + "].SetElement"
or
// We reduce generality slightly by not tracking tuple contents on list arguments beyond the first, for performance.
// TODO: Once we have TupleElementAny, this generality can be increased.
i = 0 and
exists(DataFlow::TupleElementContent tc, int j | j = tc.getIndex() |
input = "Argument[1].TupleElement[" + j.toString() + "]"
)
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
) and
output = "Argument[0].Parameter[" + i.toString() + "]" and
preservesValue = true
)
or
input = "Argument[0].ReturnValue" and
output = "ReturnValue.ListElement" and
preservesValue = true
}
}
/** A flow summary for `filter`. */
class FilterSummary extends SummarizedCallable {
FilterSummary() { this = "builtins.filter" }
override DataFlow::CallCfgNode getACall() { result = API::builtin("filter").getACall() }
override DataFlow::ArgumentNode getACallback() {
result = API::builtin("filter").getAValueReachableFromSource()
}
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[1].ListElement"
or
input = "Argument[1].SetElement"
or
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
input = "Argument[1].TupleElement[" + i.toString() + "]"
)
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
) and
(output = "Argument[0].Parameter[0]" or output = "ReturnValue.ListElement") and
preservesValue = true
}
}
/**A summary for `enumerate`. */
class EnumerateSummary extends SummarizedCallable {
EnumerateSummary() { this = "builtins.enumerate" }
override DataFlow::CallCfgNode getACall() { result = API::builtin("enumerate").getACall() }
override DataFlow::ArgumentNode getACallback() {
result = API::builtin("enumerate").getAValueReachableFromSource()
}
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[0].ListElement"
or
input = "Argument[0].SetElement"
or
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
input = "Argument[0].TupleElement[" + i.toString() + "]"
)
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
) and
output = "ReturnValue.ListElement.TupleElement[1]" and
preservesValue = true
}
}
/** A flow summary for `zip`. */
class ZipSummary extends SummarizedCallable {
ZipSummary() { this = "builtins.zip" }
override DataFlow::CallCfgNode getACall() { result = API::builtin("zip").getACall() }
override DataFlow::ArgumentNode getACallback() {
result = API::builtin("zip").getAValueReachableFromSource()
}
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(int i | exists(any(Call c).getArg(i)) |
(
input = "Argument[" + i.toString() + "].ListElement"
or
input = "Argument[" + i.toString() + "].SetElement"
or
// We reduce generality slightly by not tracking tuple contents on arguments beyond the first two, for performance.
// TODO: Once we have TupleElementAny, this generality can be increased.
i in [0 .. 1] and
exists(DataFlow::TupleElementContent tc, int j | j = tc.getIndex() |
input = "Argument[" + i.toString() + "].TupleElement[" + j.toString() + "]"
)
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
) and
output = "ReturnValue.ListElement.TupleElement[" + i.toString() + "]" and
preservesValue = true
)
}
}
// ---------------------------------------------------------------------------
// Flow summaries for container methods
// ---------------------------------------------------------------------------

View File

@@ -738,21 +738,9 @@ class PythonFunctionValue extends FunctionValue {
else result = "function " + this.getQualifiedName()
}
override int minParameters() {
exists(Function f |
f = this.getScope() and
result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault())
)
}
override int minParameters() { result = this.getScope().getMinPositionalArguments() }
override int maxParameters() {
exists(Function f |
f = this.getScope() and
if exists(f.getVararg())
then result = 2147483647 // INT_MAX
else result = count(f.getAnArg())
)
}
override int maxParameters() { result = this.getScope().getMaxPositionalArguments() }
/** Gets a control flow node corresponding to a return statement in this function */
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }

View File

@@ -21,6 +21,8 @@ private module CleartextLoggingConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "Clear-text logging of sensitive information" vulnerabilities. */

View File

@@ -21,6 +21,8 @@ private module CleartextStorageConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "Clear-text storage of sensitive information" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module CodeInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "code injection" vulnerabilities. */

View File

@@ -20,6 +20,8 @@ module CommandInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "command injection" vulnerabilities. */

View File

@@ -20,6 +20,8 @@ module CookieInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "cookie injection" vulnerabilities. */

View File

@@ -16,6 +16,8 @@ private module HeaderInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) { node instanceof HttpHeaderInjection::Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof HttpHeaderInjection::Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "HTTP Header injection" vulnerabilities. */

View File

@@ -19,6 +19,8 @@ private module LdapInjectionDnConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof DnSink }
predicate isBarrier(DataFlow::Node node) { node instanceof DnSanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "LDAP injection via the distinguished name (DN) parameter" vulnerabilities. */
@@ -30,6 +32,8 @@ private module LdapInjectionFilterConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof FilterSink }
predicate isBarrier(DataFlow::Node node) { node instanceof FilterSanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "LDAP injection via the filter parameter" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module LogInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "log injection" vulnerabilities. */

View File

@@ -56,6 +56,8 @@ module NoSqlInjectionConfig implements DataFlow::StateConfigSig {
predicate isBarrier(DataFlow::Node node) {
node = any(NoSqlSanitizer noSqlSanitizer).getAnInput()
}
predicate observeDiffInformedIncrementalMode() { any() }
}
module NoSqlInjectionFlow = TaintTracking::GlobalWithState<NoSqlInjectionConfig>;

View File

@@ -31,6 +31,8 @@ private module PamAuthorizationConfig implements DataFlow::ConfigSig {
// Flow from handle to the authenticate call in the final step
exists(VulnPamAuthCall c | c.getArg(0) = node1 | node2 = c)
}
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "PAM Authorization" vulnerabilities. */

View File

@@ -71,6 +71,8 @@ module PathInjectionConfig implements DataFlow::StateConfigSig {
stateFrom instanceof NotNormalized and
stateTo instanceof NormalizedUnchecked
}
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "path injection" vulnerabilities. */

View File

@@ -35,6 +35,11 @@ module PolynomialReDoS {
/** Gets the regex that is being executed by this node. */
abstract RegExpTerm getRegExp();
/** Gets a term within the regexp that may perform polynomial back-tracking. */
final PolynomialBackTrackingTerm getABacktrackingTerm() {
result.getRootTerm() = this.getRegExp()
}
/**
* Gets the node to highlight in the alert message.
*/

View File

@@ -17,6 +17,24 @@ private module PolynomialReDoSConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
// Diff-informed incremental mode is currently disabled for this query due to
// API limitations. The query exposes sink.getABacktrackingTerm() as an alert
// location, but there is no way to express that information through
// getASelectedSinkLocation() because there is no @location in the CodeQL
// database that corresponds to a term inside a regular expression. As a
// result, this query could miss alerts in diff-informed incremental mode.
//
// To address this problem, we need to have a version of
// getASelectedSinkLocation() that uses hasLocationInfo() instead of
// returning Location objects.
predicate observeDiffInformedIncrementalMode() { none() }
Location getASelectedSinkLocation(DataFlow::Node sink) {
result = sink.(Sink).getHighlight().getLocation()
or
result = sink.(Sink).getABacktrackingTerm().getLocation()
}
}
/** Global taint-tracking for detecting "polynomial regular expression denial of service (ReDoS)" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module ReflectedXssConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "reflected server-side cross-site scripting" vulnerabilities. */

View File

@@ -18,6 +18,14 @@ private module RegexInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
Location getASelectedSinkLocation(DataFlow::Node sink) {
result = sink.(Sink).getLocation()
or
result = sink.(Sink).getRegexExecution().getLocation()
}
}
/** Global taint-tracking for detecting "regular expression injection" vulnerabilities. */

View File

@@ -29,6 +29,12 @@ private module FullServerSideRequestForgeryConfig implements DataFlow::ConfigSig
or
node instanceof FullUrlControlSanitizer
}
predicate observeDiffInformedIncrementalMode() {
// The partial request forgery query depends on `fullyControlledRequest` to reject alerts about
// such full-controlled requests, regardless of the associated source.
none()
}
}
/**
@@ -58,6 +64,13 @@ private module PartialServerSideRequestForgeryConfig implements DataFlow::Config
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
Location getASelectedSinkLocation(DataFlow::Node sink) {
// Note: this query does not select the sink itself
result = sink.(Sink).getRequest().getLocation()
}
}
/**

View File

@@ -17,6 +17,8 @@ private module SqlInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "SQL injection" vulnerabilities. */

View File

@@ -26,6 +26,8 @@ private module StackTraceExposureConfig implements DataFlow::ConfigSig {
nodeTo = attr
)
}
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "stack trace exposure" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module TarSlipConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "tar slip" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module TemplateInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) { node instanceof Sink }
predicate isBarrierIn(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "template injection" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module UnsafeDeserializationConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "code execution from deserialization" vulnerabilities. */

View File

@@ -28,6 +28,16 @@ module UnsafeShellCommandConstructionConfig implements DataFlow::ConfigSig {
// override to require the path doesn't have unmatched return steps
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
predicate observeDiffInformedIncrementalMode() { any() }
Location getASelectedSinkLocation(DataFlow::Node sink) {
result = sink.(Sink).getLocation()
or
result = sink.(Sink).getStringConstruction().getLocation()
or
result = sink.(Sink).getCommandExecution().getLocation()
}
}
/** Global taint-tracking for detecting "shell command constructed from library input" vulnerabilities. */

View File

@@ -32,6 +32,8 @@ private module UrlRedirectConfig implements DataFlow::StateConfigSig {
) {
any(UrlRedirect::AdditionalFlowStep a).step(nodeFrom, stateFrom, nodeTo, stateTo)
}
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "URL redirection" vulnerabilities. */

View File

@@ -33,6 +33,8 @@ module NormalHashFunction {
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
sensitiveDataExtraStepForCalls(node1, node2)
}
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on sensitive data" vulnerabilities. */
@@ -63,6 +65,8 @@ module ComputationallyExpensiveHashFunction {
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
sensitiveDataExtraStepForCalls(node1, node2)
}
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on passwords" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module XmlBombConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "XML bomb" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module XpathInjectionConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "Xpath Injection" vulnerabilities. */

View File

@@ -17,6 +17,8 @@ private module XxeConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "XML External Entity (XXE)" vulnerabilities. */

View File

@@ -62,7 +62,7 @@ module HeuristicNames {
*/
string maybeAccountInfo() {
result = "(?is).*acc(ou)?nt.*" or
result = "(?is).*(puid|username|userid|session(id|key)).*" or
result = "(?is).*(puid|user.?name|user.?id|session.?(id|key)).*" or
result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*"
}
@@ -71,8 +71,8 @@ module HeuristicNames {
* a password or an authorization key.
*/
string maybePassword() {
result = "(?is).*pass(wd|word|code|phrase)(?!.*question).*" or
result = "(?is).*(auth(entication|ori[sz]ation)?)key.*"
result = "(?is).*pass(wd|word|code|.?phrase)(?!.*question).*" or
result = "(?is).*(auth(entication|ori[sz]ation)?).?key.*"
}
/**