Repository navigation
fix(https_fn): stop logging callable request contents on invalid bodies - #330
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.
Matches the Node SDK. Names are logged with %r so a key cannot add log lines.
A JSON list, string or number body, an empty body, or malformed JSON returned 500 INTERNAL instead of 400 INVALID_ARGUMENT.
…body Also removes the unreachable body checks in the auth blocking handler, one of which still passed the request body to the logger.
ajperel
left a comment
There was a problem hiding this comment.
⚠️ Disclaimer: This draft review was generated by an experimental multi-perspective AI review agent. Please verify all findings before acting on them.
Code Review Summary: firebase/firebase-functions-python PR #330
🏛️ Architectural Assessment & Approach Verdict
- Verdict: APPROVE APPROACH
- Assessment: The PR cleanly resolves Issue #328. Switching from
request.jsontorequest.get_json(silent=True)and addingisinstance(body, dict)fixes unhandledBadRequestandTypeErrorexceptions on malformed or non-object payloads, properly returning HTTP 400 (INVALID_ARGUMENT) instead of HTTP 500 (INTERNAL). Crucially, it stops logging sensitive request body values before authentication. - Architectural Trade-Off (Key Logging vs. Count-Only Logging):
- The author justified logging key names with
%rfor log-forging defense and Node SDK parity (extraKeys.join(", ")infirebase-functions/src/common/providers/https.ts). - Using
%rformat specifiers invokes Python'srepr(), escaping control characters (\n,\r,\t,\x00) and preventing log line splitting in stdout and structured Cloud Logging sinks. - Regarding Key Limits: In the Node SDK and Python SDK, there is no precedent for capping key lists with arbitrary limits like
10—Node logs all extra keys, and platform payload limits (10MB/32MB) constrain request size. However, if maintainers wish to be completely immune to untrusted input in logs (as suggested in issue #328), logging key counts (len(body) - 1) rather than key names is the cleanest zero-allocation alternative.
- The author justified logging key names with
🟢 Strengths & LGTM Aspects
- Accurate HTTP Status Mapping: Replaces unhandled exceptions with clean boolean validation, allowing
_on_call_handlerto consistently return HTTP 400INVALID_ARGUMENT. - Log Injection Hardening: Using
%rformat specifiers escapes control characters, neutralizing log line forging attacks. - Dead-Code Pruning: Safely removes redundant, buggy validation logic in
_identity_fn.pythat duplicated_util.valid_on_call_requestand contained an unredacted logging site. - Comprehensive Negative Test Suite: Adds 6 unit tests in
tests/test_util.pycovering malformed JSON, non-object payloads, empty bodies, and newline escaping.
🔴 Overview of Findings
- 0 Blocking Issues: The PR is safe, functionally correct, and ready to land.
- 2 Suggestions:
- O(1) Extra-Key Detection: Check
len(body) > 1before list comprehension allocation once"data" in bodyis verified. - Add Happy-Path Unit Test: All added unit tests verify rejection paths; positive assertions verifying
_on_call_valid_bodyreturnsTruewithout logging on valid bodies should be included.
- O(1) Extra-Key Detection: Check
| if extra_keys: | ||
| _logging.warning("Request body has extra fields: %r", extra_keys) | ||
| return False | ||
| return True |
There was a problem hiding this comment.
🟡 Suggestion: O(1) extra-keys check on valid requests
Since line 131 already verifies that "data" in body, checking len(body) > 1 avoids allocating extra_keys = [key ...] on valid requests with no extra fields:
| if extra_keys: | |
| _logging.warning("Request body has extra fields: %r", extra_keys) | |
| return False | |
| return True | |
| if len(body) > 1: | |
| extra_keys = [key for key in body.keys() if key != "data"] | |
| _logging.warning("Request body has extra fields: %r", extra_keys) | |
| return False |
There was a problem hiding this comment.
Nice suggestion, saves building the list for valid requests, added in ac74442
|
|
||
| def test_on_call_valid_body_rejects_malformed_json_without_logging_it(caplog): | ||
| with caplog.at_level(logging.WARNING): | ||
| assert not _valid_body("{secretValue") |
There was a problem hiding this comment.
🟡 Suggestion: Add happy-path test for valid callable bodies
All 6 new unit tests assert rejection (assert not _valid_body(...)). Consider adding a positive test case verifying that valid bodies return True and produce zero warning logs:
def test_on_call_valid_body_accepts_valid_payloads(caplog):
for body in (
'{"data": {}}',
'{"data": "value"}',
'{"data": 123}',
'{"data": null}',
'{"data": [1, 2, 3]}',
):
caplog.clear()
with caplog.at_level(logging.WARNING):
assert _valid_body(body)
assert caplog.text == ""
Fixes #328.
When an
on_callfunction got a malformed request, its warning log could include values from the request body, before any auth check. Bodies that were not a JSON object also returned 500 instead of 400.This logs only key names, never values or the body, and returns 400 for those bodies.
%r, so a newline in a name cannot add fake log lines. Node also logs the names.Tested with new unit tests, the emulator, and deployed callable and task queue functions.