Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Tighten deserialization allowlist regex to require full-string match - #66499

Merged
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist
May 18, 2026
Merged

Tighten deserialization allowlist regex to require full-string match#66499
vatsrahul1001 merged 3 commits into
apache:mainfrom
potiuk:serde-fullmatch-allowlist

Conversation

@potiuk

Copy link
Copy Markdown
Member

Tighten the deserialization allowlist ([core] allowed_deserialization_classes_regexp)
to use re.fullmatch() instead of re.match(). Previously a pattern such as
airflow\.models\.Variable admitted not only the intended class but also
airflow.models.Variable_Maliciousre.match only anchors at the start
of the string. Using fullmatch requires the pattern to match the entire
classname, eliminating the prefix-bypass footgun.

Updated the config description so admins know patterns are full-match and
that .* is needed for prefix-style allowances. Updated the existing test
that relied on prefix-match semantics, and added a dedicated test for the
bypass scenario.

Compatibility note for reviewers

This is a behaviour change for any deployment that configured
allowed_deserialization_classes_regexp with patterns relying on
prefix-match semantics (e.g. airflow\.models\. to mean "any class under
airflow.models"). Such deployments need to add .* to the pattern.
The default value is empty, so out-of-the-box deployments are unaffected.
Default off, admin-only config — leaving the newsfragment decision to the
reviewer.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.7 (1M context)

Generated-by: Claude Opus 4.7 (1M context) following the guidelines

potiuk added a commit to potiuk/airflow that referenced this pull request May 7, 2026
potiuk added 2 commits May 17, 2026 21:44
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
@potiuk
potiukforce-pushed the serde-fullmatch-allowlist branch from 89c56f5 to fa69b78CompareMay 17, 2026 19:44
@potiuk

Copy link
Copy Markdown
MemberAuthor

I'd love to get this one merged — and would love it in 3.2.2 if it's not too late. cc @vatsrahul1001 (3.2.2 RM)


Drafted-by: Claude Code (Opus 4.7); reviewed by @potiuk before posting

@vatsrahul1001

vatsrahul1001 commented May 18, 2026

Copy link
Copy Markdown
Contributor

LGTM!, love to get another pair of eyes @amoghrajesh@kaxil@ashb

@vatsrahul1001
vatsrahul1001 merged commit 80f1ab4 into apache:mainMay 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

vatsrahul1001 added a commit that referenced this pull request May 18, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…tring match (#66499) (#67096)
* Tighten deserialization allowlist regex to use full-string match
The ``allowed_deserialization_classes_regexp`` allowlist used ``re.match()``,
which only anchors at the start of the string. A pattern like
``airflow\.models\.Variable`` therefore also admitted classnames such as
``airflow.models.Variable_Malicious``. Switch to ``re.fullmatch()`` so the
admin's pattern matches the entire classname; document the semantics in
the config description so operators know to use ``.*`` for prefix-style
allowances.
* Add newsfragment for #66499
---------
(cherry picked from commit 80f1ab4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@potiuk@vatsrahul1001@Lee-W@amoghrajesh