Python: Port UndefinedGlobal.ql

Uses most of the machinery we have built up by now to implement this, so
hopefully it should be an easy comparison.

Also, extends the `monkeyPatchedBuiltin` with an additional way to patch
built-ins (because this was needed to get a test to pass).

No test changes.
This commit is contained in:
Taus
2026-02-27 14:46:12 +00:00
parent 39049e3fcc
commit 27bcd490c5
2 changed files with 31 additions and 22 deletions

View File

@@ -2005,10 +2005,18 @@ module DuckTyping {
* Holds if `name` is monkey-patched into the builtins module.
*/
predicate monkeyPatchedBuiltin(string name) {
exists(DataFlow::AttrWrite aw |
API::moduleImport("builtins").getAValueReachableFromSource().asExpr() =
aw.getObject().asExpr() and
aw.getAttributeName() = name
any(DataFlow::AttrWrite aw)
.writes(API::moduleImport("builtins").getAValueReachableFromSource(), name, _)
or
// B.__dict__["name"] = value
exists(SubscriptNode subscr |
subscr.isStore() and
subscr.getObject() =
API::moduleImport("builtins")
.getMember("__dict__")
.getAValueReachableFromSource()
.asCfgNode() and
subscr.getIndex().getNode().(StringLiteral).getText() = name
)
}

View File

@@ -11,9 +11,10 @@
*/
import python
private import LegacyPointsTo
private import semmle.python.dataflow.new.internal.ImportResolution
private import semmle.python.dataflow.new.internal.DataFlowDispatch
private import semmle.python.ApiGraphs
private import semmle.python.types.ImportTime
import Variables.MonkeyPatched
import Loop
predicate guarded_against_name_error(Name u) {
@@ -32,10 +33,13 @@ predicate guarded_against_name_error(Name u) {
}
predicate contains_unknown_import_star(Module m) {
exists(ImportStar imp | imp.getScope() = m |
exists(ModuleValue imported | imported.importedAs(imp.getImportedModuleName()) |
not imported.hasCompleteExportInfo()
)
exists(ImportStar imp, Module imported |
imp.getScope() = m and
ImportResolution::getModuleImportedByImportStar(imp) = imported
|
// The imported module dynamically creates attributes, so we can't
// enumerate its exports.
exists(Function f | f.getName() = "__getattr__" and f.getScope() = imported)
)
}
@@ -60,9 +64,9 @@ predicate undefined_use_in_function(Name u) {
)
) and
not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and
not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and
not globallyDefinedName(u.getId()) and
not exists(SsaVariableWithPointsTo var | var.getAUse().getNode() = u and not var.maybeUndefined()) and
not ImportResolution::module_export(u.getEnclosingModule(), u.getId(), _) and
not DuckTyping::globallyDefinedName(u.getId()) and
not Reachability::maybeUndefined(u) and
not guarded_against_name_error(u) and
not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__")
}
@@ -70,20 +74,18 @@ predicate undefined_use_in_function(Name u) {
predicate undefined_use_in_class_or_module(Name u) {
exists(GlobalVariable v | u.uses(v)) and
not u.getScope().getScope*() instanceof Function and
exists(SsaVariableWithPointsTo var | var.getAUse().getNode() = u | var.maybeUndefined()) and
Reachability::maybeUndefined(u) and
not guarded_against_name_error(u) and
not exists(ModuleValue m | m.getScope() = u.getEnclosingModule() | m.hasAttribute(u.getId())) and
not u.getEnclosingModule().(ImportTimeScope).definesName(u.getId()) and
not ImportResolution::module_export(u.getEnclosingModule(), u.getId(), _) and
not (u.getEnclosingModule().isPackageInit() and u.getId() = "__path__") and
not globallyDefinedName(u.getId())
not DuckTyping::globallyDefinedName(u.getId())
}
predicate use_of_exec(Module m) {
exists(Exec exec | exec.getScope() = m)
or
exists(CallNode call, FunctionValue exec | exec.getACall() = call and call.getScope() = m |
exec = Value::named("exec") or
exec = Value::named("execfile")
)
API::builtin(["exec", "execfile"]).getACall().getScope() = m
}
predicate undefined_use(Name u) {
@@ -92,11 +94,10 @@ predicate undefined_use(Name u) {
or
undefined_use_in_function(u)
) and
not monkey_patched_builtin(u.getId()) and
not DuckTyping::monkeyPatchedBuiltin(u.getId()) and
not contains_unknown_import_star(u.getEnclosingModule()) and
not use_of_exec(u.getEnclosingModule()) and
not exists(u.getVariable().getAStore()) and
not u.(ExprWithPointsTo).pointsTo(_) and
not probably_defined_in_loop(u)
}