Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

RedactedIO leaks secret bytes to stdout

Abierto Apto para principiantes
#75,069 1 comentario 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@ShivanshhhG ya está trabajando en esto.

Desde el 19/10/2026.

  • #75077 de @ShivanshhhG — abierto
  • #75129 de @muditagrawal2007 — abierto

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
82/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
backend, security

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 test is 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

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de apache/airflow

Todos los issues de apache/airflow

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.