mirror of
https://github.com/github/codeql.git
synced 2026-07-03 10:35:29 +02:00
Flips the Python dataflow trunk from the legacy CFG (semmle/python/Flow.qll) and legacy ESSA SSA (semmle/python/essa/*) to the new shared CFG facade (semmle.python.controlflow.internal.Cfg) and the new SSA adapter (semmle.python.dataflow.new.internal.SsaImpl), both introduced additively in the preceding PRs in this stack. This is the trunk-flip equivalent of the original draft PR #21894 (kept around as documentation), rebased on top of the four preparatory PRs: P1: Remove AstNode.getAFlowNode() and rewrite callers (#21919). P2: Qualify Flow.qll's AST references with Py:: prefix (#21920). P3: Add new shared-CFG-backed control flow graph (#21921). P4: Add new shared-SSA-backed SSA adapter (#21923). The Python dataflow library (semmle/python/dataflow/new/) now imports the new CFG facade and SSA adapter. All CFG-typed predicates (ControlFlowNode, CallNode, BasicBlock, NameNode, AttrNode, ...) are qualified with the Cfg:: prefix; SSA references switch from EssaVariable/EssaDefinition to SsaImpl::Definition/SourceVariable. GuardNode is redesigned to use the new CFG's outcome-node model (isAfterTrue / isAfterFalse) instead of the legacy ConditionBlock + flipped indirection. Only BarrierGuard<...> is preserved as public API. Framework files (Bottle, FastApi, Django, Tornado, Pyramid, Stdlib, ...) are updated to take CFG nodes from the new facade. A handful of dataflow consistency tweaks for the new CFG: - Augmented-assignment targets are treated as both load and store. - 'from X import *' produces uncertain SSA writes for unknown names. - CFG nodes are canonicalised so dataflow does not see equivalent pre/post-order pairs as distinct nodes. Two AST tweaks for the new CFG: - AstNodeImpl: omit PEP 695 type-parameter names from FunctionDefExpr / ClassDefExpr children. - ImportResolution: drop the legacy essa import. Test churn (~175 files): reblessed library- and query-test .expected files reflect slightly different CFG granularity, different toString output, and a handful of true alert deltas in security queries. Verification: all 367 lib + src + consistency-queries compile clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
156 lines
4.9 KiB
Plaintext
156 lines
4.9 KiB
Plaintext
/**
|
|
* Provides predicates for measuring the quality of the call graph, that is,
|
|
* the number of calls that could be resolved to a target.
|
|
*/
|
|
|
|
import python
|
|
import meta.MetaMetrics
|
|
private import LegacyPointsTo
|
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
|
|
|
newtype TTarget =
|
|
TFunction(Function func) or
|
|
TClass(Class cls)
|
|
|
|
class Target extends TTarget {
|
|
/** Gets a textual representation of this element. */
|
|
abstract string toString();
|
|
|
|
/** Gets the location of this dataflow call. */
|
|
abstract Location getLocation();
|
|
|
|
/** Whether this target is relevant. */
|
|
predicate isRelevant() { exists(this.getLocation().getFile().getRelativePath()) }
|
|
}
|
|
|
|
class TargetFunction extends Target, TFunction {
|
|
Function func;
|
|
|
|
TargetFunction() { this = TFunction(func) }
|
|
|
|
override string toString() { result = func.toString() }
|
|
|
|
override Location getLocation() { result = func.getLocation() }
|
|
|
|
Function getFunction() { result = func }
|
|
}
|
|
|
|
class TargetClass extends Target, TClass {
|
|
Class cls;
|
|
|
|
TargetClass() { this = TClass(cls) }
|
|
|
|
override string toString() { result = cls.toString() }
|
|
|
|
override Location getLocation() { result = cls.getLocation() }
|
|
|
|
Class getClass() { result = cls }
|
|
}
|
|
|
|
/**
|
|
* A call that is (possibly) relevant for analysis quality.
|
|
* See `IgnoredFile` for details on what is excluded.
|
|
*/
|
|
class RelevantCall extends Cfg::CallNode {
|
|
RelevantCall() { not this.getLocation().getFile() instanceof IgnoredFile }
|
|
}
|
|
|
|
/** Provides classes for call-graph resolution by using points-to. */
|
|
module PointsToBasedCallGraph {
|
|
/** A call that can be resolved by points-to. */
|
|
class ResolvableCall extends RelevantCall {
|
|
Value targetValue;
|
|
|
|
ResolvableCall() { targetValue.getACall().getNode() = this.getNode() }
|
|
|
|
/** Gets a resolved target of this call. */
|
|
Target getTarget() {
|
|
result.(TargetFunction).getFunction() = targetValue.(CallableValue).getScope()
|
|
or
|
|
result.(TargetClass).getClass() = targetValue.(ClassValue).getScope()
|
|
}
|
|
}
|
|
|
|
/** A call that cannot be resolved by points-to. */
|
|
class UnresolvableCall extends RelevantCall {
|
|
UnresolvableCall() { not this instanceof ResolvableCall }
|
|
}
|
|
|
|
/**
|
|
* A call that can be resolved by points-to, where the resolved target is relevant.
|
|
* Relevant targets include:
|
|
* - source code of the project
|
|
*/
|
|
class ResolvableCallRelevantTarget extends ResolvableCall {
|
|
ResolvableCallRelevantTarget() {
|
|
exists(Target target | target = this.getTarget() |
|
|
exists(target.getLocation().getFile().getRelativePath())
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call that can be resolved by points-to, where the resolved target is not considered relevant.
|
|
* See `ResolvableCallRelevantTarget` for the definition of relevance.
|
|
*/
|
|
class ResolvableCallIrrelevantTarget extends ResolvableCall {
|
|
ResolvableCallIrrelevantTarget() { not this instanceof ResolvableCallRelevantTarget }
|
|
}
|
|
}
|
|
|
|
/** Provides classes for call-graph resolution by using type-tracking. */
|
|
module TypeTrackingBasedCallGraph {
|
|
private import semmle.python.dataflow.new.internal.DataFlowDispatch as TT
|
|
|
|
/** A call that can be resolved by type-tracking. */
|
|
class ResolvableCall extends RelevantCall {
|
|
ResolvableCall() {
|
|
TT::resolveCall(this, _, _)
|
|
or
|
|
TT::resolveClassCall(this, _)
|
|
}
|
|
|
|
/** Gets a resolved target of this call. */
|
|
Target getTarget() {
|
|
exists(TT::CallType ct, Function targetFunc |
|
|
TT::resolveCall(this, targetFunc, ct) and
|
|
not ct instanceof TT::CallTypeClass and
|
|
targetFunc = result.(TargetFunction).getFunction()
|
|
)
|
|
or
|
|
// TT::resolveCall only holds when the call can be resolved to a function.
|
|
// Since points-to just says the call goes directly to the class itself, and
|
|
// type-tracking based wants to resolve this to the constructor, which might not
|
|
// exist. So to do a proper comparison, we don't require the call to be resolve to
|
|
// a specific function.
|
|
TT::resolveClassCall(this, result.(TargetClass).getClass())
|
|
}
|
|
}
|
|
|
|
/** A call that cannot be resolved by type-tracking. */
|
|
class UnresolvableCall extends RelevantCall {
|
|
UnresolvableCall() { not this instanceof ResolvableCall }
|
|
}
|
|
|
|
/**
|
|
* A call that can be resolved by type-tracking, where the resolved callee is relevant.
|
|
* Relevant targets include:
|
|
* - source code of the project
|
|
*/
|
|
class ResolvableCallRelevantTarget extends ResolvableCall {
|
|
ResolvableCallRelevantTarget() {
|
|
exists(Target target | target = this.getTarget() |
|
|
exists(target.getLocation().getFile().getRelativePath())
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call that can be resolved by type-tracking, where the resolved target is not considered relevant.
|
|
* See `ResolvableCallRelevantTarget` for the definition of relevance.
|
|
*/
|
|
class ResolvableCallIrrelevantTarget extends ResolvableCall {
|
|
ResolvableCallIrrelevantTarget() { not this instanceof ResolvableCallRelevantTarget }
|
|
}
|
|
}
|