From 009427196600a73c9beceabc3fcd89e8f0a58213 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 23 Feb 2026 13:36:31 +0000 Subject: [PATCH] Python: Port WrongNameForArgumentInClassInstantiation.ql --- .../new/internal/DataFlowDispatch.qll | 6 ++++ ...rongNameForArgumentInClassInstantiation.ql | 30 ++++++++++++++++--- ...meForArgumentInClassInstantiation.expected | 8 ++--- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index 2aaa33de159..fbb31837274 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -2111,4 +2111,10 @@ module DuckTyping { ) ) } + + /** + * Gets the `__init__` function that will be invoked when `cls` is constructed, + * resolved according to the MRO. + */ + Function getInit(Class cls) { result = invokedFunctionFromClassConstruction(cls, "__init__") } } diff --git a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql index 5b50855dcdf..984114111f5 100644 --- a/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql +++ b/python/ql/src/Classes/WrongNameForArgumentInClassInstantiation.ql @@ -15,12 +15,34 @@ */ import python -import Expressions.CallArgs -private import LegacyPointsTo +private import semmle.python.dataflow.new.internal.DataFlowDispatch -from Call call, ClassValue cls, string name, FunctionValue init +/** + * Holds if `name` is a legal argument name for calling `init`. + */ +bindingset[name] +predicate isLegalArgumentName(Function init, string name) { + exists(init.getArgByName(name)) + or + init.hasKwArg() +} + +/** + * Holds if `call` constructs class `cls` and passes a keyword argument `name` + * that does not correspond to any parameter of `cls.__init__`. + */ +predicate illegally_named_parameter(Call call, Class cls, string name) { + exists(Function init | + resolveClassCall(call.getAFlowNode(), cls) and + init = DuckTyping::getInit(cls) and + name = call.getANamedArgumentName() and + not isLegalArgumentName(init, name) + ) +} + +from Call call, Class cls, string name, Function init where illegally_named_parameter(call, cls, name) and - init = get_function_or_initializer(cls) + init = DuckTyping::getInit(cls) select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", init, init.getQualifiedName() diff --git a/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.expected b/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.expected index 4bdb1134776..cf89ddfc44f 100644 --- a/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.expected +++ b/python/ql/test/query-tests/Classes/Arguments/WrongNameForArgumentInClassInstantiation.expected @@ -1,4 +1,4 @@ -| wrong_arguments.py:65:1:65:7 | F0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:4:5:4:26 | Function F0.__init__ | F0.__init__ | -| wrong_arguments.py:66:1:66:7 | F1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:8:5:8:36 | Function F1.__init__ | F1.__init__ | -| wrong_arguments.py:67:1:67:12 | F2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:12:5:12:30 | Function F2.__init__ | F2.__init__ | -| wrong_arguments.py:92:1:92:27 | F6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:28:5:28:30 | Function F6.__init__ | F6.__init__ | +| wrong_arguments.py:65:1:65:7 | F0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ | +| wrong_arguments.py:66:1:66:7 | F1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ | +| wrong_arguments.py:67:1:67:12 | F2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:12:5:12:30 | Function __init__ | F2.__init__ | +| wrong_arguments.py:92:1:92:27 | F6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |