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

   Follow-up to #72683, which guarded the *value* `json.loads` returned but not 
the parse itself. Closes the class behind #49035 and #62354.
   
   `action_logging` reads the request body to record it in the audit log, and 
parsed it with no error handling. Whether that mattered depended on the route: 
where the route declares a body, FastAPI parses and validates it first, so a 
bad body is a 4xx and this dependency never runs. Where the route declares 
**no** body -- the deletes, the favourite toggles, the backfill pause/cancel 
actions -- FastAPI never touches the body, so this was the only parse of it and 
the error escaped as a 500. **The action was lost with it.**
   
   Against a local api-server, before:
   
   ```
   POST   /api/v2/variables            -> 201
   GET    /api/v2/variables/e2e_probe  -> 200   (exists)
   DELETE /api/v2/variables/e2e_probe  -> 500
   GET    /api/v2/variables/e2e_probe  -> 200   (still there -- the delete was 
lost)
   ```
   
   ```
   File ".../airflow/api_fastapi/logging/decorators.py", line 207, in log_action
     parsed_body = await request.json()
   File ".../starlette/requests.py", line 265, in json
     self._json = json.loads(body)
   json.decoder.JSONDecodeError: Expecting property name enclosed in double 
quotes: line 1 column 2 (char 1)
   ```
   
   After, for each input class that makes `json.loads` raise:
   
   ```
     malformed      DELETE -> 204   follow-up GET -> 404  (deleted)
     invalid_utf8   DELETE -> 204   follow-up GET -> 404  (deleted)
     oversized_int  DELETE -> 204   follow-up GET -> 404  (deleted)
     deeply_nested  DELETE -> 204   follow-up GET -> 404  (deleted)
   ```
   
   ## Design rationale
   
   **A body this dependency cannot read is recorded as an access without a 
body, not rejected.** It runs before the route so an access is logged whether 
or not the route succeeds -- the ordering is deliberate, per the discussion on 
#64641 -- which also means it must never be the reason a request fails. 
Returning 400 instead would put validation of a field the API does not declare 
inside the audit logger.
   
   **The catch is broad, matching what FastAPI does around the same call.** 
`json.loads` raises `JSONDecodeError` for malformed input, `UnicodeDecodeError` 
for invalid UTF-8, a bare `ValueError` for an integer over 
`int_max_str_digits`, and `RecursionError` for deeply nested input. Enumerating 
types is how this line collected three earlier fixes that each missed the next 
shape, so it now matches FastAPI's own chain, which ends in `except Exception`.
   
   **`requires_access_backfill` parses the same body and suppressed only 
`JSONDecodeError`**, so it re-raised on the others. A body it cannot read names 
no Dag, which is the state it already handles, so it now suppresses them the 
same way -- no authorization outcome changes. Reachable only when `backfill_id` 
is absent or unparsable, since a valid one is authorized from the path without 
reading the body.
   
   **The audit row keeps naming the resource.** With no readable body, the 
masking branches fell back to an empty dict, so a variable or connection delete 
recorded `{"method": "DELETE"}` and lost the only record of what it destroyed 
-- `Log` has no column for it. Visible in one log, old row then new:
   
   ```
   extra='{"method": "DELETE"}'
   extra='{"variable_key": "e2e_malformed", "method": "DELETE"}'
   ```
   
   ## Gotchas
   
   The test enumerates the bodyless routes from the app and sends every input 
class above, rather than naming the endpoint of the latest report. It requires 
an audit row per route as well as a non-500, because a route rejected by an 
earlier dependency would otherwise pass while proving nothing -- `PUT 
/parseDagFile/{file_token}` does exactly that with a placeholder token, so it 
is exempted explicitly rather than silently.
   


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