-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LGTM.com - false positive: Python metaclass #2445
Comments
Came here to report the same. This affects:
Here is some simple example code that demonstrates the issue (Python 3.x):
This code will produce a false positive, because even though the class This may seem obscure but the SageMath project uses custom class calls extensively in its dynamic class system. A large number of its reported errors are false positives due to this (we are discussing disabling them globally which I think is a perfectly acceptable work-around for now. I've never poked around at how CodeQL works so I have no idea how hard it is to check this case, since with metaclasses you can have more-or-less arbitrary behavior. |
We have the same issue over in https://github.com/intel/dffml class BaseConfigurableMetaClass(type, abc.ABC):
def __new__(cls, name, bases, props, module=None):
# Create the class
cls = super(BaseConfigurableMetaClass, cls).__new__(
cls, name, bases, props
)
# Wrap __init__
setattr(cls, "__init__", cls.wrap(cls.__init__))
return cls
@classmethod
def wrap(cls, func):
"""
If a subclass of BaseConfigurable is passed keyword arguments, convert
them into the instance of the CONFIG class.
"""
@functools.wraps(func)
def wrapper(self, config: Optional[BaseConfig] = None, **kwargs):
if config is not None and len(kwargs):
raise ConfigAndKWArgsMutuallyExclusive
elif config is None and hasattr(self, "CONFIG") and kwargs:
try:
config = self.CONFIG(**kwargs)
except TypeError as error:
error.args = (
error.args[0].replace(
"__init__", f"{self.CONFIG.__qualname__}"
),
)
raise
if config is None:
raise TypeError(
"__init__() missing 1 required positional argument: 'config'"
)
return func(self, config)
return wrapper
class BaseConfigurable(metaclass=BaseConfigurableMetaClass):
"""
Class which produces a config for itself by providing Args to a CMD (from
dffml.util.cli.base) and then using a CMD after it contains parsed args to
instantiate a config (deriving from BaseConfig) which will be used as the
only parameter to the __init__ of a BaseDataFlowFacilitatorObject.
"""
def __init__(self, config: Type[BaseConfig]) -> None:
"""
BaseConfigurable takes only one argument to __init__,
its config, which should inherit from BaseConfig. It shall be a object
containing any information needed to configure the class and it's child
context's.
"""
self.config = config
str_config = str(self.config)
self.logger.debug(
str_config if len(str_config) < 512 else (str_config[:512] + "...")
) |
Hi folks. This must have fallen off our radar. @BekaValentine I know you have been looking at these queries just now, could you have a look whether this would be easy to fix? |
Part of the problem is that it's probably not easy to fix, since metaclasses can do almost anything here that would make static analysis close to impossible. Maybe at best, if a class has a custom metaclass, change this to a warning. |
Correct me if I'm wrong but it should be possible to at least fix the expected number of arguments? |
Not necessarily. I mean, I could write a metaclass that randomizes the number of expected arguments on every call. At best you can say that it's unknowable. |
@embray that's essentially what our query is doing. If we can statically determine the minimum number of arguments required, and can see you're not providing enough, we will give you an alert. So if you have the function definitions def foo(*args):
...
def bar(x, *args):
... we can give an alert if you call Currently we don't take metaclasses into account, so once we do that, we hopefully shouldn't see these False Positives anymore. |
That doesn't necessarily help because you can't even know what methods are involved in initializing instances of a given class. Here's an example of what I'm talking about:
Obviously this case is completely pathological, but one can think of other cases, such with classes that are created at runtime (Sage for example is full of these). |
I've tried to recreate this issue but I've been unable to. I have a branch here: https://github.com/BekaValentine/ql/tree/python-false-positive-metaclass-call Just over a week ago, a PR merged that updated the API that's used in these queries, which may be the reason, but I also tried to recreate this on the older API, and was still unable to get it working: https://github.com/BekaValentine/ql/tree/python-false-positive-metaclass-call-objectapi @RasmusWL have you had any luck recreating this? |
My issue comes from the fact that the metaclass instantiates an object with a default arg1 if no parameters are passed. Which doesn't really fit with the "at least this number" check |
Due to codeql's lack of understanding of metaclasses Related: github/codeql#2445 Signed-off-by: John Andersen <john.s.andersen@intel.com>
Due to codeql's lack of understanding of metaclasses Related: github/codeql#2445 Signed-off-by: John Andersen <john.s.andersen@intel.com>
Due to codeql's lack of understanding of metaclasses Related: github/codeql#2445 Signed-off-by: John Andersen <john.s.andersen@intel.com>
Due to codeql's lack of understanding of metaclasses Related: github/codeql#2445 Signed-off-by: John Andersen <john.s.andersen@intel.com>
Due to codeql's lack of understanding of metaclasses Related: github/codeql#2445 Signed-off-by: John Andersen <john.s.andersen@intel.com>
Description of the false positive
Classes may have metaclasses in Python. If the metaclass has a
__call__
function, then calling the class should require the same number of arguments than the__call__
function of the metaclass has.See https://docs.python.org/3/reference/datamodel.html#object.call
URL to the alert on the project page on LGTM.com: https://lgtm.com/projects/g/secdev/scapy/snapshot/4d06eb2812a98a1001478bf06e033fbbb2afbef7/files/scapy/sendrecv.py#L844
See:
If you need more help understanding the issue ping me and I'll make a snippet. This code isn't the easiest to catch on
The text was updated successfully, but these errors were encountered: