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]