MahathirMohammadShuvo opened a new pull request, #72063:
URL: https://github.com/apache/airflow/pull/72063

   `RedactedIO` exists so that output produced by user code is masked before it
   reaches stdout — its docstring says *"Writes to stdout will be redacted."*
   `write()` applies `redact()`, but `writelines()` forwarded straight to the
   target, so anything written that way was printed verbatim.
   
   ```python
   import contextlib, sys
   from airflow.sdk._shared.secrets_masker import RedactedIO, mask_secret
   
   mask_secret("hunter2-topsecret")
   with contextlib.redirect_stdout(RedactedIO()):
       print("hunter2-topsecret")                     # ***
       sys.stdout.writelines(["hunter2-topsecret\n"]) # hunter2-topsecret  <- 
before
   ```
   
   `writelines` now redacts each element the same way `write()` does.
   
   ### Scope
   
   The one call site in the repo is `task_test()` in
   `airflow-core/src/airflow/cli/commands/task_command.py`, so this is reachable
   from `airflow tasks test`, which deliberately routes task output up to the
   console handler for local debugging.
   
   This is a masking gap rather than a privilege issue: triggering it requires 
DAG
   code, and the security model already treats the DAG author as a trusted role 
who
   can reach these values through the Execution API. Filing it here rather than
   through the security process follows the precedent of #68422 and #65912, 
which
   fixed comparable masking gaps in the same files as ordinary PRs.
   
   ### Two things worth flagging
   
   - **Non-string elements now stringify instead of raising `TypeError`.** That 
is a
     behaviour change beyond the reported bug, and it is deliberate: it makes
     `writelines` behave exactly like `write()`, which already does 
`str(redact(s))`.
   - **A secret split across elements still passes through** —
     `writelines(["hunter2-", "topsecret\n"])`. `write()` has the same property 
when
     a secret is split across calls, so this is a pre-existing limit of 
chunk-wise
     redaction rather than something introduced here. The multi-line case
     (`writelines(pem_key.splitlines(True))`) is the one asymmetry: 
`write(pem_key)`
     masks it because the pattern is stored as one `re.escape`d string. Fixing 
that
     would mean buffering the whole iterable and giving up laziness, which 
seemed
     out of proportion to this fix — happy to follow up separately if you would 
like
     it closed.
   
   ### Testing
   
   - `test_writelines` added next to the existing `test_write`; it fails 
without the
     source change.
   - `pytest shared/secrets_masker/tests/secrets_masker/test_secrets_masker.py` 
—
     **142 passed**, 1 skipped, 1 xfailed. One failure, 
`test_redact_filehandles`,
     is a Windows-only artifact of my local environment (`FileNotFoundError:
     '/dev/null'`) and fails identically on a clean `main` checkout here.
   - `ruff check` and `ruff format --diff` clean on both changed files.
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes (please specify the tool below)
   
   Generated-by: Claude Code following [the 
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
   


-- 
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]

Reply via email to