RedactedIO leaks secret bytes to stdout
Los mantenedores suelen responder en 1 día
@ShivanshhhG ya está trabajando en esto.
Desde el 19/10/2026.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 82/100
Línea de trabajo
Empieza leyendo RedactedIO.write() y writelines() en shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py, junto con el manejo de tipos referenciado de _redact y las pruebas de RedactedIO. Reproduce el caso de bytes con un secreto registrado y comprueba después que se enmascaren tanto la salida de bytes como la de bytearray, mientras se mantiene intacto el comportamiento con cadenas.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Describe the bug
RedactedIO.write() and RedactedIO.writelines() are supposed to mask registered secrets before any output reaches the terminal. They do — for str values. For bytes (and bytearray) values, the registered secret is written to stdout in cleartext, wrapped in Python's bytes repr (b'…').
The only production call site today is airflow tasks test (airflow-core/src/airflow/cli/commands/task_command.py:539), which wraps task execution in redirect_stdout(RedactedIO()). Any task that calls sys.stdout.writelines([b"…secret…"]) — common with subprocess wrappers, log buffers, and some logging libraries — will have its secrets printed verbatim.
Reproducer
from airflow.sdk._shared.secrets_masker import mask_secret, RedactedIO
mask_secret("hunter2")
# str — masked
RedactedIO().write("here is hunter2")
# -> here is ***
# bytes containing the same secret — NOT masked
RedactedIO().write(b"here is hunter2")
# -> b'here is hunter2' <-- secret leaked as bytes repr
Equivalently, after the airflow tasks test wrapper is active inside a Dag:
import sys
def my_task(**_):
sys.stdout.writelines([b"authorization: Bearer hunter2\n"])
# Writes "authorization: Bearer b'authorization: Bearer hunter2\n'"
# to the console. The token is now in scrollback.
Expected
RedactedIO().write(b"...hunter2...") should produce the same masked output as RedactedIO().write("...hunter2...") — ...***....
Actual
The bytes are passed to redact() first, and redact() does not understand bytes — they fall through _redact's type checks to the default return item at secrets_masker.py:430, so the bytes object is returned unchanged. The str(...) wrapper in both write() (line 720-722) and writelines() (line 724-725) then converts the bytes to their b'…' repr, which writes the cleartext secret to the underlying stream.
Why this matters
RedactedIO exists so that output produced by user code is masked before it reaches stdout. Its docstring says "Writes to stdout will be redacted." That promise is silently broken for the bytes path. Because the wrapper is applied to the entire task run, any user code that ever produces a bytes write through stdout is affected — and there's no error to warn the caller, the secret is just printed.
This was introduced into the bytes path of writelines() by #72063 (which deliberately propagated the behavior of the pre-existing write()). The write() method has had the same issue for a long time, but the only recently-added bytes-supporting code path is writelines().
Proposed fix
Decode bytes/bytearray to UTF-8 (with replacement) before calling redact() in both methods, so the existing pattern-based masker can see the content. Something like:
def _redact_io_value(value) -> str:
if isinstance(value, (bytes, bytearray)):
value = value.decode("utf-8", errors="replace")
return str(redact(value))
Both write() and writelines() would call this helper, keeping them symmetric. The permissive behavior for non-string types is preserved (per the explicit intent of #72063), but bytes are now actually scanned for registered secrets.
Area
- secrets_masker
- CLI (
airflow tasks testis the only reachable production call site)
Tested Airflow version
main (commit 935bac04f0)
- Lenguaje dominante
- Python
- Estrellas
- 47.3k
- Forks
- 18.1k
- Merge medio
- 19 h 8 min
- PR fusionados (30 d)
- 640
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de apache/airflow
-
update virtualenv to 21.14.12 or later to resolve emSign security issuePosiblemente ocupada @arifrhm la tomó hace 1 día. Abiertokind:bug needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
apache/airflow#75858 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
`@task.pyspark` does not template `config_kwargs` and `conn_id`Posiblemente ocupada @sdaberdaku la tomó hace 3 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 79/100
Los mantenedores suelen responder en 1 día
-
Trigger UI clamps Param min/max instead of rejecting out-of-range valuesPosiblemente ocupada @muditagrawal2007 la tomó hace 3 días. Abiertokind:bug needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
apache/airflow#75747 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Deadline Alert: No support for SmtpNotifierPosiblemente ocupada @LiaoAB la tomó hace 5 días. Abiertokind:bug needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
apache/airflow#75452 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Task supervisor raises TypeError when a task log event has a key named target or msg, orphaning the task process and recording a successful task as failedPosiblemente ocupada @liwenjie200543 la tomó hace 11 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
apache/airflow#75017 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de apache/airflow
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
pytest-dev/pytest-html#1085 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 90/100
pollen-robotics/reachy_mini#1527 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 66/100
Los mantenedores suelen responder en 2 días
-
Variadic arguments that aren't objects are passed as objects (stringWithFormat_ with %d / %f)Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
A seed and its negative give the same run, so realizations of a negative seed repeat each otherAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
Los mantenedores suelen responder en 1 día