-
Notifications
You must be signed in to change notification settings - Fork 17.4k
Validate deferred trigger classpath resolves to a BaseTrigger subclass #69792
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Reject deferred-task trigger classpaths that do not resolve to a ``BaseTrigger`` subclass before the class is instantiated in the triggerer, so a deferred task cannot cause an arbitrary importable callable to be invoked in the triggerer process. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1696,8 +1696,22 @@ def get_trigger_by_classpath(self, classpath: str) -> type[BaseTrigger]: | |
| """ | ||
| Get a trigger class by its classpath ("path.to.module.classname"). | ||
|
|
||
| The resolved object must be a :class:`~airflow.triggers.base.BaseTrigger` | ||
| subclass. This is validated before the class is cached and, crucially, | ||
| before it is ever instantiated in ``create_triggers`` -- ``classpath`` | ||
| originates from the (attacker-influenceable) deferred-task payload, so | ||
| without this check an arbitrary importable callable could be invoked in | ||
| the triggerer process. | ||
|
|
||
| Uses a cache dictionary to speed up lookups after the first time. | ||
| """ | ||
| if classpath not in self.trigger_cache: | ||
| self.trigger_cache[classpath] = import_string(classpath) | ||
| trigger_class = import_string(classpath) | ||
| if not (isinstance(trigger_class, type) and issubclass(trigger_class, BaseTrigger)): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be pre-import check, unfortunately - by the time we get here the class is already imported, and the whole idea is to avoid even importing it - because just importing it can have side-effects. I am not sure if that one is even reasonably doable - because in order to get class hierarchy you need to import it. Just AST parsing will not solve it. So I am not sure if that is solving such defense-in-depth is even doable. |
||
| raise TypeError( | ||
| f"The trigger classpath {classpath!r} does not resolve to a " | ||
| f"{BaseTrigger.__module__}.{BaseTrigger.__qualname__} subclass; " | ||
| f"refusing to load it." | ||
| ) | ||
| self.trigger_cache[classpath] = trigger_class | ||
| return self.trigger_cache[classpath] | ||
Uh oh!
There was an error while loading. Please reload this page.