wilsonjefferson opened a new issue, #71275:
URL: https://github.com/apache/airflow/issues/71275

   ### Under which category would you file this issue?
   
   Airflow Core
   
   ### Apache Airflow version
   
   3.3.0
   
   ### What happened and how to reproduce it?
   
   **Issue Description**
   
   When a Variable's key name matches the sensitive-keyword list, `GET 
/api/v2/variables`
   correctly returns `***` for string values, but returns non-string values 
(int, float,
   bool) in cleartext.
   
   `SecretsMasker._redact_all` is the fail-closed path used when a key name is 
judged
   sensitive, but it only replaces `str`, recurses into containers, and returns 
every
   other type unchanged:
   
   ```python
   # airflow/_shared/secrets_masker/secrets_masker.py:323-347
   def _redact_all(self, item, depth, max_depth=MAX_RECURSION_DEPTH, *, 
replacement="***"):
       if depth > max_depth or isinstance(item, str):
           return replacement
       if isinstance(item, dict): ...
       if isinstance(item, (tuple, set)): ...
       if isinstance(item, list): ...
       return item          # <-- non-str scalars pass through unredacted
   ```
   
   It is reached from `_redact` line 358:
   
   ```python
   if name and self.should_hide_value_for_key(name):
       return self._redact_all(item, depth, max_depth, replacement=replacement)
   ```
   
   The comment at lines 353-355 states that key-name-based redaction "must fail 
closed at
   any nesting level". It fails closed on depth, but not on type.
   
   **Steps to reproduce**
   
   1. Create Variables whose names contain a sensitive keyword:
   
   ```bash
   airflow variables set test-password-a "abcd"
   airflow variables set test-password-b "1234"
   airflow variables set test-password-c "12ab"
   airflow variables set test-password-d "1234.5"
   airflow variables set test-password-e "true"
   ```
   
   2. Read them back via the API (or view Admin -> Variables in the UI):
   
   ```bash
   curl -H "Authorization: Bearer $TOKEN" \
     "http://<airflow>/api/v2/variables?limit=100"
   ```
   
   3. Observed:
   
   | value    | returned  |
   |----------|-----------|
   | `abcd`   | `***`     |
   | `12ab`   | `***`     |
   | `1234`   | `1234`    |
   | `1234.5` | `1234.5`  |
   | `true`   | `true`    |
   
   Values that are valid JSON scalars (int, float, bool) are returned unmasked; 
values
   that remain strings are masked correctly.
   
   **Not affected: task logs**
   
   Task logs mask correctly for numeric values, because that path registers the 
secret
   value as a substring pattern rather than going through `_redact_all`:
   
   ```python
   @task
   def leak_test():
       from airflow.sdk import Variable
       print("numeric:", Variable.get("test-password-num"))   # 12345678 -> ***
       print("string:",  Variable.get("test-password-long"))  # abcdefgh -> ***
   ```
   
   Both are masked. (Use values of 5+ characters — shorter ones hit the
   "Skipping masking for a secret as it's too short (<5 chars)" guard.)
   
   The same code exists at the same line numbers in
   `airflow/sdk/_shared/secrets_masker/secrets_masker.py`.
   
   ### What you think should happen instead?
   
   Once `should_hide_value_for_key(name)` returns True, the value should be 
redacted
   regardless of its Python type. A numeric PIN, account number, or all-digit 
API key
   stored under a `*_password` / `*_token` key is a realistic case, and the 
current
   behaviour silently returns it in cleartext while an equivalent alphanumeric 
value
   is masked.
   
   A possible fix is to invert the type check in `_redact_all` so that 
containers are
   traversed and everything else is replaced:
   
   ```python
   if depth > max_depth or not isinstance(item, (dict, tuple, set, list)):
       return replacement
   ```
   
   I'm not sure whether non-strings are deliberately preserved for `merge()`, 
which
   restores original values where `***` is unchanged — a maintainer should 
confirm
   that before applying anything like the above.
   
   ### Operating System
   
   PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" 
VERSION_ID="12" VERSION="12 (bookworm)" VERSION_CODENAME=bookworm ID=debian
   
   ### Deployment
   
   Docker-Compose
   
   ### Apache Airflow Provider(s)
   
   _No response_
   
   ### Versions of Apache Airflow Providers
   
   _No response_
   
   ### Official Helm Chart version
   
   Not Applicable
   
   ### Kubernetes Version
   
   _No response_
   
   ### Helm Chart configuration
   
   _No response_
   
   ### Docker Image customizations
   
   Based on apache/airflow:3.3.0, with apache-airflow-providers-keycloak==0.8.2 
installed. No changes to core Airflow.
   
   ### Anything else?
   
   ccurs every time. Deployment uses the Keycloak auth manager, but the code 
path is in core and is not auth-manager-specific. Python 3.13.
   
   ### Are you willing to submit PR?
   
   - [ ] Yes I am willing to submit a PR!
   
   ### Code of Conduct
   
   - [x] I agree to follow this project's [Code of 
Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)
   


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