Skip to content

CVE-2026-49297: Verified Reproduction

CVE-2026-49297: Apache Airflow Google provider path traversal via GCS object names

CVE-2026-49297 is verified against apache-airflow-providers-google · pip. Affected versions: before 22.2.1 (both bugs present in 22.0.0; GCSTimeSpanFileTransformOperator fixed in 22.1.0 via PR #67509; GCSToSFTPOperator fixed in 22.2.0 via PR #67667). Fixed in 22.2.1. Vulnerability class: Path Traversal. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00257.

REPRO-2026-00257 apache-airflow-providers-google · pip Path Traversal Jul 6, 2026 CVE entry .txt
Severity
HIGH
CVSS
8.1
Confidence
HIGH
Reproduced in
56m 55s
Tool calls
352
Spend
$10.75
01 · Overview

What Is CVE-2026-49297?

CVE-2026-49297 is a medium-severity path traversal (CWE-22) in Apache Airflow's Google provider transfer operators, GCSToSFTPOperator and GCSTimeSpanFileTransformOperator, which can write downloaded blobs outside their configured destination directory. Pruva reproduced it (reproduction REPRO-2026-00257).

02 · Severity & CVSS

CVE-2026-49297 Severity & CVSS Score

CVE-2026-49297 is rated high severity, with a CVSS base score of 8.1 out of 10.

HIGH threat level
8.1 / 10 CVSS base
Weakness CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected apache-airflow-providers-google Versions

apache-airflow-providers-google · pip versions before 22.2.1 (both bugs present in 22.0.0; GCSTimeSpanFileTransformOperator fixed in 22.1.0 via PR #67509; GCSToSFTPOperator fixed in 22.2.0 via PR #67667) are affected.

How to Reproduce CVE-2026-49297

$ pruva-verify REPRO-2026-00257
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00257/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-49297

Security impact — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

GCS object name 'subdir/../../escaped_N.txt' returned by a bucket listing API for source_object 'subdir/*'

Attack chain
  1. airflow tasks test
  2. GCSToSFTPOperator.execute()
  3. GCSHook.list/download over GCS JSON API
  4. SFTPHook.store_file
How the agent worked 865 events · 352 tool calls · 57 min
57 minDuration
352Tool calls
227Reasoning steps
865Events
2Dead-ends
Agent activity over 57 min
Support
39
Hypothesis
2
Repro
471
Judge
57
Variant
118
Coding
172
0:0056:55

Root Cause and Exploit Chain for CVE-2026-49297

Versions: Reproduced in apache-airflow-providers-google==22.1.0; the ticket states the issue is fixed in 22.2.1.

CVE-2026-49297 is a path traversal vulnerability in Apache Airflow's Google provider transfer operators, demonstrated here through GCSToSFTPOperator. When the operator processes a wildcard source_object, it obtains object names from the GCS bucket listing API and joins each returned object name directly to the configured SFTP destination_path. In vulnerable provider version 22.1.0, a GCS object name such as subdir/../../escaped_1.txt is accepted and copied to the SFTP server as destination_path/subdir/../../escaped_1.txt, which normalizes outside the intended destination directory. Fixed version 22.2.1 rejects the same object name before any SFTP write.

  • Package/component affected: apache-airflow-providers-google, specifically airflow.providers.google.cloud.transfers.gcs_to_sftp.GCSToSFTPOperator and the same object-name handling pattern described for related GCS transfer operators.
  • Affected versions: Reproduced in apache-airflow-providers-google==22.1.0; the ticket states the issue is fixed in 22.2.1.
  • Risk level and consequences: Medium. A principal who can write object names into a source GCS bucket consumed by an Airflow DAG can cause the DAG to write outside the configured SFTP destination directory. This can overwrite or create unintended files on the SFTP target host with the permissions available to the Airflow/SFTP workflow.

Impact Parity

  • Disclosed/claimed maximum impact: Arbitrary file overwrite/path traversal through attacker-controlled GCS object names returned by the bucket listing API.
  • Reproduced impact from this run: Full product-path file write outside the configured SFTP destination_path using a real Airflow DAG task invocation, real GCSHook.list/download calls against a local GCS JSON API emulator, and a real SFTP protocol server.
  • Parity: full
  • Not demonstrated: The proof stops at creation of an attacker-controlled file outside destination_path; it does not chain the write into code execution or privilege escalation, which were not required by the ticket.

Root Cause

In vulnerable GCSToSFTPOperator, wildcard processing calls GCSHook.list() and iterates over object names returned by the GCS API. For each object, _resolve_destination_path() returns os.path.join(self.destination_path, source_object) without canonicalizing the result or checking that it remains inside destination_path. GCS object names are attacker-controlled strings for anyone who can write to the source bucket, and object names may contain .. path segments. Therefore a listed object such as subdir/../../escaped_1.txt is transformed into a remote SFTP path under destination_path/subdir/../../escaped_1.txt, which normalizes to a sibling of destination_path.

The fixed provider release 22.2.1 adds a containment check in _resolve_destination_path(): it normalizes the joined destination and rejects paths that escape the configured base directory, raising ValueError before SFTPHook.store_file() is called. The exact upstream fix commit was not provided in the ticket; this reproduction uses the fixed release named by the ticket (apache-airflow-providers-google==22.2.1) as the negative control.

Reproduction Steps

  1. Run bundle/repro/reproduction_steps.sh.
  2. The script uses the prepared project cache when available, ensures two Python environments for apache-airflow-providers-google==22.1.0 and ==22.2.1, and downloads/reuses fsouza/fake-gcs-server as a local GCS JSON API endpoint.
  3. For each attempt, bundle/repro/product_harness.py:
    • starts fake-gcs-server on localhost,
    • creates a real GCS bucket/object via google-cloud-storage, with object name subdir/../../escaped_N.txt,
    • starts a Paramiko SFTP server on localhost,
    • writes a real Airflow DAG containing GCSToSFTPOperator, and
    • invokes it through airflow tasks test cve_2026_49297_repro_dag copy_malicious_gcs_object_to_sftp.
  4. The script runs two vulnerable attempts and two fixed attempts. Expected evidence:
    • vulnerable 22.1.0 attempts create sftp_root/escaped_N.txt outside sftp_root/inbox,
    • fixed 22.2.1 attempts raise ValueError: Refusing to copy GCS object ... escapes configured destination_path, and
    • fixed attempts perform no SFTP file write.

Evidence

Key runtime artifacts generated by the script:

  • Main proof log: bundle/logs/reproduction_steps.log
  • Vulnerable attempt summaries:
    • bundle/logs/attempt_22.1.0_1.json
    • bundle/logs/attempt_22.1.0_2.json
  • Fixed attempt summaries:
    • bundle/logs/attempt_22.2.1_1.json
    • bundle/logs/attempt_22.2.1_2.json
  • Per-attempt GCS API traces:
    • bundle/artifacts/product_test_22.1.0_1/gcs_api_trace.json
    • bundle/artifacts/product_test_22.1.0_2/gcs_api_trace.json
    • bundle/artifacts/product_test_22.2.1_1/gcs_api_trace.json
    • bundle/artifacts/product_test_22.2.1_2/gcs_api_trace.json
  • Per-attempt Airflow task logs:
    • bundle/artifacts/product_test_22.1.0_1/airflow_tasks_test.log
    • bundle/artifacts/product_test_22.1.0_2/airflow_tasks_test.log
    • bundle/artifacts/product_test_22.2.1_1/airflow_tasks_test.log
    • bundle/artifacts/product_test_22.2.1_2/airflow_tasks_test.log
  • Structured verdict: bundle/repro/validation_verdict.json
  • Runtime manifest: bundle/repro/runtime_manifest.json

Representative evidence from the latest successful run:

  • The vulnerable Airflow task log shows the real operator processing the malicious object and resolving an escaping SFTP path:

    Executing copy of gs://malicious-bucket/subdir/../../escaped_1.txt to .../sftp_root/inbox/subdir/../../escaped_1.txt

  • The vulnerable attempt JSON records the out-of-directory write:

    "escaped_file_exists": true, "file_inside_destination": false, and an SFTP open operation whose remote path is .../inbox/subdir/../../escaped_1.txt while the local file path is .../sftp_root/escaped_1.txt.

  • The fixed Airflow task log records the negative control:

    ValueError: Refusing to copy GCS object 'subdir/../../escaped_1.txt': resolved destination '.../sftp_root/escaped_1.txt' escapes configured destination_path '.../sftp_root/inbox'.

  • The fixed attempt JSON records no escaped file and no SFTP write operations:

    "escaped_file_exists": false, "exception_type": "ValueError", and "sftp_operations": [].

Environment details captured by the script include exact installed packages and source file paths for the operator and hook, for example:

  • apache-airflow==3.2.2
  • apache-airflow-providers-google==22.1.0 for the vulnerable run
  • apache-airflow-providers-google==22.2.1 for the fixed run
  • apache-airflow-providers-sftp==5.8.2

Recommendations / Next Steps

  • Upgrade apache-airflow-providers-google to 22.2.1 or later.
  • Keep the fixed containment check in _resolve_destination_path() and apply equivalent checks to any other operator that maps GCS object names to local or remote filesystem paths.
  • Add regression tests that use GCS object names containing .., absolute-looking path components, and mixed directory separators where applicable.
  • Prefer containment checks based on normalized/canonical paths and reject any resolved path outside the configured destination root before creating directories or opening remote files.

Additional Notes

  • The final reproduction script was run successfully end-to-end and produced fresh current-run artifacts.
  • The proof uses a local GCS JSON API emulator rather than monkey-patching GCSHook; Airflow's real GCSHook.list() and GCSHook.download() cross an HTTP API boundary.
  • The proof uses a real Paramiko SFTP protocol server and records accepted SFTP connections and file-open operations.
  • The script is idempotent: each attempt uses a clean per-attempt artifact directory, a new GCS emulator instance, and a new SFTP server instance.

CVE-2026-49297 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:22
0:00
session startedaccounts/fireworks/models/kimi-k2p7-code · CVE-2026-49297 · REPRO-20
0:03
0:05
web search
0:08
0:09
0:11
0:13
0:14
web search
0:16
0:17
0:20
0:21
web search
0:22
0:26
0:27
web search
0:30
0:32
0:35
web search
0:44
0:45
0:50
0:54
1:04
1:05
web search
1:20
1:21
extract_facts
no facts extracted
1:22
1:22
08 · How to Fix

How to Fix CVE-2026-49297

Upgrade apache-airflow-providers-google · pip to 22.2.1 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-49297 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-49297

Which apache-airflow-providers-google versions are affected by CVE-2026-49297, and where is it fixed?

Versions before 22.2.1 are affected (both operators had the bug present in 22.0.0; GCSTimeSpanFileTransformOperator was fixed in 22.1.0 via PR #67509, and GCSToSFTPOperator was fixed in 22.2.0 via PR #67667). It is fully fixed in apache-airflow-providers-google 22.2.1.

How severe is CVE-2026-49297?

It is rated medium severity: a principal who can write object names into a source GCS bucket consumed by an Airflow DAG can cause the DAG to write or overwrite files outside the configured destination directory on the SFTP target or worker host.

Does exploiting CVE-2026-49297 require compromising Airflow directly?

No — the attacker needs write access to the source GCS bucket that an Airflow DAG ingests from, not access to Airflow itself. This is why deployments ingesting from buckets writable by less-trusted principals are exposed.

How can I reproduce CVE-2026-49297?

Download the verified script from this page and run it in an isolated environment against apache-airflow-providers-google==22.1.0. Create a GCS object with a traversal name such as subdir/../../escaped_1.txt in a bucket used by a wildcard GCSToSFTPOperator source_object, confirm the file lands outside the configured SFTP destination_path, and confirm the fixed 22.2.1 provider rejects the same object name before any SFTP write.
11 · References

References for CVE-2026-49297

Authoritative sources for CVE-2026-49297 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.