Files
codeql/python/ql/lib/semmle/python/frameworks/Pycurl.qll
yoff 5081d8171e Python: switch dataflow library to new (shared) CFG + SSA
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>
2026-06-24 08:41:23 +00:00

106 lines
3.4 KiB
Plaintext

/**
* Provides classes modeling security-relevant aspects of the `pycurl` PyPI package.
*
* See
* - https://pypi.org/project/pycurl/
* - https://pycurl.io/docs/latest/
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.data.ModelsAsData
/**
* INTERNAL: Do not use.
*
* Provides models for the `pycurl` PyPI package.
*
* See
* - https://pypi.org/project/pycurl/
* - https://pycurl.io/docs/latest/
*/
module Pycurl {
/**
* Provides models for the `pycurl.Curl` class
*
* See https://pycurl.io/docs/latest/curl.html.
*/
module Curl {
/** Gets a reference to the `pycurl.Curl` class. */
API::Node classRef() {
result = API::moduleImport("pycurl").getMember("Curl")
or
result = ModelOutput::getATypeNode("pycurl.Curl~Subclass").getASubclass*()
}
/** Gets a reference to an instance of `pycurl.Curl`. */
private API::Node instance() { result = classRef().getReturn() }
/** Gets a reference to `pycurl.Curl.setopt`. */
private API::Node setopt() { result = instance().getMember("setopt") }
/** Gets a reference to the constant `pycurl.Curl.SSL_VERIFYPEER`. */
private API::Node sslverifypeer() {
result = API::moduleImport("pycurl").getMember("SSL_VERIFYPEER") or
result = instance().getMember("SSL_VERIFYPEER")
}
/**
* When the first parameter value of the `setopt` function is set to `pycurl.URL`,
* the second parameter value is the request resource link.
*
* See http://pycurl.io/docs/latest/curlobject.html#pycurl.Curl.setopt.
*/
private class OutgoingRequestCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode
{
OutgoingRequestCall() {
this = setopt().getACall() and
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = "URL"
}
override DataFlow::Node getAUrlPart() {
result in [super.getArg(1), super.getArgByName("value")]
}
override string getFramework() { result = "pycurl.Curl" }
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
none()
}
}
/**
* When the first parameter value of the `setopt` function is set to `SSL_VERIFYPEER` or `SSL_VERIFYHOST`,
* the second parameter value disables or enable SSL certifiacte verification.
*
* See http://pycurl.io/docs/latest/curlobject.html#pycurl.Curl.setopt.
*/
private class CurlSslCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode {
CurlSslCall() {
this = setopt().getACall() and
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
}
override DataFlow::Node getAUrlPart() { none() }
override string getFramework() { result = "pycurl.Curl" }
override predicate disablesCertificateValidation(
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
) {
sslverifypeer().getAValueReachableFromSource() = super.getArg(0) and
(
super.getArg(1).asExpr().(IntegerLiteral).getValue() = 0
or
super.getArg(1).asExpr().(BooleanLiteral).booleanValue() = false
) and
(disablingNode = this and argumentOrigin = super.getArg(1))
}
}
}
}