Repository navigation
fix(https_fn): stop logging callable request contents on invalid bodies - #330
IzaakGough wants to merge 2 commits into
Conversation
Invalid callable bodies are logged before auth is checked, so log only the count of extra fields and never the body.
There was a problem hiding this comment.
Code Review
This pull request updates the _on_call_valid_body function to prevent logging sensitive request body content, opting instead to log the count of extra fields. Corresponding unit tests were added to verify this behavior. The reviewer suggested adding a type check to ensure request.json is a dictionary to avoid potential runtime errors and recommended expanding test coverage to include non-dictionary JSON inputs to ensure robust error handling.
| if "data" not in request.json: | ||
| _logging.warning("Request body is missing data.", request.json) | ||
| _logging.warning("Request body is missing data.") | ||
| return False | ||
|
|
||
| extra_keys = {key: request.json[key] for key in request.json.keys() if key != "data"} | ||
| if len(extra_keys) != 0: | ||
| _logging.warning( | ||
| "Request body has extra fields: %s", | ||
| "".join(f"{key}: {value}," for (key, value) in extra_keys.items()), | ||
| ) | ||
| # Request content is logged before auth is checked, so log neither keys nor values. | ||
| extra_field_count = sum(1 for key in request.json.keys() if key != "data") | ||
| if extra_field_count: | ||
| _logging.warning("Request body has extra fields (count=%d).", extra_field_count) | ||
| return False |
There was a problem hiding this comment.
If the request body is a JSON list (e.g., ["data", "extra"]) or a non-dictionary type (e.g., an integer or string), checking "data" not in request.json or calling request.json.keys() will raise a TypeError or AttributeError. Checking isinstance(request.json, dict) first ensures that the request body is a dictionary before accessing its keys or checking for the presence of "data" keys.
| if "data" not in request.json: | |
| _logging.warning("Request body is missing data.", request.json) | |
| _logging.warning("Request body is missing data.") | |
| return False | |
| extra_keys = {key: request.json[key] for key in request.json.keys() if key != "data"} | |
| if len(extra_keys) != 0: | |
| _logging.warning( | |
| "Request body has extra fields: %s", | |
| "".join(f"{key}: {value}," for (key, value) in extra_keys.items()), | |
| ) | |
| # Request content is logged before auth is checked, so log neither keys nor values. | |
| extra_field_count = sum(1 for key in request.json.keys() if key != "data") | |
| if extra_field_count: | |
| _logging.warning("Request body has extra fields (count=%d).", extra_field_count) | |
| return False | |
| if not isinstance(request.json, dict) or "data" not in request.json: | |
| _logging.warning("Request body is missing data.") | |
| return False | |
| # Request content is logged before auth is checked, so log neither keys nor values. | |
| extra_field_count = sum(1 for key in request.json.keys() if key != "data") | |
| if extra_field_count: | |
| _logging.warning("Request body has extra fields (count=%d).", extra_field_count) | |
| return False |
| def test_on_call_valid_body_does_not_log_body_when_data_is_missing(caplog): | ||
| for body in ('{"secretKey": "secretValue"}', '["secretValue"]'): | ||
| caplog.clear() | ||
| with caplog.at_level(logging.WARNING): | ||
| assert not _valid_body(body) | ||
| assert "Request body is missing data." in caplog.text | ||
| assert "secretValue" not in caplog.text |
There was a problem hiding this comment.
Add additional test cases to verify that non-dictionary JSON bodies (such as lists containing the string "data" or raw integers) are handled gracefully without raising unhandled exceptions.
| def test_on_call_valid_body_does_not_log_body_when_data_is_missing(caplog): | |
| for body in ('{"secretKey": "secretValue"}', '["secretValue"]'): | |
| caplog.clear() | |
| with caplog.at_level(logging.WARNING): | |
| assert not _valid_body(body) | |
| assert "Request body is missing data." in caplog.text | |
| assert "secretValue" not in caplog.text | |
| def test_on_call_valid_body_does_not_log_body_when_data_is_missing(caplog): | |
| for body in ('{"secretKey": "secretValue"}', '["secretValue"]', '["data", "secretValue"]', '123'): | |
| caplog.clear() | |
| with caplog.at_level(logging.WARNING): | |
| assert not _valid_body(body) | |
| assert "Request body is missing data." in caplog.text | |
| assert "secretValue" not in caplog.text |
Matches the Node SDK. Names are logged with %r so a key cannot add log lines.
Fixes #328.
When an
on_callfunction got a malformed request, its warning log could include values from the request body, and this happens before any auth check.This logs only the names of any extra fields, and never their values or the body.
%r, so a name containing a newline cannot add fake log lines. Node also logs the names.Tested with new unit tests and in the Functions emulator. Not tested on a deployed function.