Conversation
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 github#21894 (kept around as documentation), rebased on top of the four preparatory PRs: P1: Remove AstNode.getAFlowNode() and rewrite callers (github#21919). P2: Qualify Flow.qll's AST references with Py:: prefix (github#21920). P3: Add new shared-CFG-backed control flow graph (github#21921). P4: Add new shared-SSA-backed SSA adapter (github#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>
The `Cfg::ControlFlowNode` facade re-exports the shared CFG library's `dominates`/`strictlyDominates` predicates, which are declared `bindingset[this, that]` + `pragma[inline_late]` and are meant to be used as bound-pair membership checks. The facade wrappers dropped these annotations (using plain `pragma[inline]`), so even though the only callers — the `with` / `async with` taint steps in DataFlowPrivate.qll and TaintTrackingPrivate.qll — bind both endpoints, the optimizer was free to materialise `Cfg::ControlFlowNode.strictlyDominates/1` as a full O(nodes^2) relation over the (larger) shared-CFG node set. On some projects this dominated analysis time entirely (DCA showed e.g. ICTU/quality-time and biosimulations regressing ~75-160x). Restoring `bindingset[this, other]` + `pragma[inline_late]` on the wrappers turns the predicate back into a bound-pair check and is result-preserving (only binding annotations change, the predicate body is unchanged). Reproduced on ICTU/quality-time: full python-security-extended suite went from stalling >20min on `strictlyDominates` to completing in ~6min; all ControlFlow and dataflow/coverage library tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Document the public expression adapter and apply the canonical QL annotation ordering required by the formatter. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 529363f5-bc7d-4f0b-9f47-e03ba9aa0cdf
Reproduce the Airflow-shaped loss of section/key identity through layered configuration getters. The concrete non-sensitive key is documented as SPURIOUS while concrete sensitive and dynamic keys remain positive controls. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The sensitive-name heuristic marks calls such as _get_config_value_from_secret_backend as secret sources. Interprocedural summaries then route those sources through every layered configuration getter result without retaining the guard correlation for each concrete section/key pair. This reproduced Airflow clear-text logging false positives for core.EXECUTOR and core.DAGS_FOLDER. Recognize fully resolved configuration lookups whose only value selectors are unique literal section and key arguments, and expose them as sanitizers to the clear-text logging, clear-text storage, and weak sensitive-data hashing analyses. The implementation checks every resolved target and preserves sensitive callee names and configuration-specific secret indicators. This belongs in SensitiveDataSources rather than core summary routing: the summarized path is topologically valid, while the missing fact is semantic precision for a name-based sensitive-source heuristic. Core dataflow therefore remains conservative for all other analyses. Dynamic or ambiguous selectors, sensitive concrete names, direct secret-named getters, and calls with additional value arguments remain flowing. Tests cover PASSWORD, FERNET_KEY, PASSWORD_FILE, dynamic keys, a sensitive fallback, and the Airflow-shaped safe routes. The refinement intentionally relies on conventional section/key parameter names and does not attempt arbitrary application registry reasoning. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
yoff
force-pushed
the
yoff/python-shared-cfg-dataflow-flip
branch
from
September 22, 2026 11:38
f2a6889 to
f7774ef
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
Experimental draft stacked on #21925. This must not merge independently of its parent.
The base was revalidated immediately before creation at
github/codeql:yoff/python-shared-cfg-dataflow-flip=1a8e317b4a328bea1059453a2ab3ba6eada09e3f.Causal evidence
This is not an Airflow model correction. Python's generic
SensitiveFunctionCallheuristic marks_get_config_value_from_secret_backend(...)as asecretsource because the resolved function name matches the sensitive-name regular expression. Global summaries then preserve the topological return path through Airflow's_get_env_var_option/_get_secret_option,Configuration.get,getlist, andget_mandatory_list_value, but they do not retain the control correlation between(section, key) in self.sensitive_config_valuesand each caller's concrete arguments.Consequently,
core.EXECUTORandcore.DAGS_FOLDERcan inherit the secret source even though those pairs are not members of Airflow's runtime sensitive-key registry and cannot take the secret-backend branches. A secret-backend getter being reachable for some registered keys does not imply that every concrete key handled by the shared getter is sensitive.The first commit reproduces this with an Airflow-shaped local flow and a passing
SPURIOUSexpectation. The second commit removes that annotation after the precision fix.Scope
The change deliberately leaves core dataflow and summary routing unchanged: those paths are topologically valid and useful to other analyses. Instead,
SensitiveDataSourcesidentifies a known non-sensitive configuration lookup only when:sectionandkeyparameters;The clear-text logging, clear-text storage, and weak sensitive-data hashing customizations expose those nodes through their existing sanitizer extension points. There are no Airflow names or key values in the implementation.
Exact Airflow evidence
On a locally extracted CodeQL database for
apache/airflow@a9da0f7fb48dc7526b2745be3e8fe64e1c775da2:core.EXECUTORfamily atexecutor_loader.py:225/234is removed;core.DAGS_FOLDERfamily atutils/file.py:68/96is removed;(section, key)pairs; andThe DCA report's
cli_parser.py:64row is the motivatingcore.EXECUTORroute. The local extraction did not reproduce that exact sink location, so its evidence remains the DCA comparison plus the independently reproduced shared configuration route; the exact local database did reproduce and remove the related executor and file-path families above.Conservative controls
Tests retain flow for:
PASSWORD,FERNET_KEY, andPASSWORD_FILEselectors;Ambiguous or unresolved calls also remain conservative because every resolved target must satisfy the refinement.
Limitations
This is still a name-based heuristic refinement. It intentionally recognizes only configuration APIs using conventional
section/keyformal names and does not attempt arbitrary application registry reasoning. Calls with additional value inputs are excluded because the sanitizer blocks the complete lookup result, not one incoming edge. The broad Airflow result delta is why this remains an experimental draft despite the zero-overlap registry audit.Validation
python/ql/test/library-tests/dataflow/sensitive-datapython/ql/test/query-tests/Security/CWE-312-CleartextLoggingpython/ql/test/query-tests/Security/CWE-312-CleartextStoragepython/ql/test/query-tests/Security/CWE-312-CleartextStorage-py3python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing