Fix operators declaring template_fields/template_ext as bare strings - #70916
Conversation
A bare string is a sequence of characters, so "chat_id" declares the
fields "c", "h", "a"... rather than "chat_id".
Airflow papers over this for template_fields by wrapping a string in a
list, at the cost of a UserWarning on every task build. template_ext gets
no such fallback: ".bat" expands to (".", "b", "a", "t"), so any templated
value ending in one of those characters is mistaken for a script path and
its contents silently loaded in its place.
potiuk
left a comment
There was a problem hiding this comment.
Good sweep, and the template_ext case is worth calling out separately because it is meaningfully worse than the template_fields one.
template_ext = ".bat" iterates as (".", "b", "a", "t"), and resolve_template_files does:
if isinstance(content, str) and content.endswith(tuple(self.template_ext)):
setattr(self, field, env.loader.get_source(env, content)[0])
except Exception:
log.exception("Failed to resolve template field %r", field)So for CmdOperator any command ending in ., b, a or t is treated as a script path — ls -lt, pip install requests, echo a all qualify. Usually the loader fails and it is merely logged, but if the string happens to resolve inside the Dag folder or template_searchpath, the command is silently replaced by that file's contents and executed. Unlike template_fields, there is no wrapping fallback and no warning, so nothing tells you it happened.
Two of the three are example dags, so they mislead by being copied rather than by running; TelegramFileOperator is the one that ships, where the effect is a UserWarning on every task build and a wrong class attribute.
Adding TestTelegramFileOperator with both the corrected tuple and an actual render is the right coverage. Not testing the example dags is fine — that is not the convention for them.
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
A bare string is a sequence of characters, so "chat_id" declares the fields "c", "h", "a"... rather than "chat_id".
Airflow papers over this for template_fields by wrapping a string in a list, at the cost of a UserWarning on every task build. template_ext gets no such fallback: ".bat" expands to (".", "b", "a", "t"), so any templated value ending in one of those characters is mistaken for a script path and its contents silently loaded in its place.
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Opus 5) following the guidelines
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.