Skip to content

Meta token refresh failures log Graph's raw response body via exc_info=True #605

Description

@hyoshi

What happens

A Meta token refresh that fails writes Meta's raw HTTP response body to the configure log.

_call_refresh_api embeds the whole response body in the exception message:

# mureo/auth.py:532-535
if resp.status_code != 200:
    raise ValueError(
        f"Meta token refresh failed with status {resp.status_code}: " f"{resp.text}"
    )

and refresh_meta_token_if_needed logs that with a full traceback:

# mureo/auth.py:449-453
try:
    new_token, new_obtained_at = await _call_refresh_api(credentials)
except Exception:
    logger.warning("Failed to refresh Meta Ads token", exc_info=True)
    return credentials

resp.text is unbounded content returned by Graph. Until #581 there was no logging handler, so this went nowhere. Now it lands in ~/.mureo/logs/configure.log.

Why it matters

This is the same shape as #603: raw exception text logged via exc_info=True around a token-bearing HTTP call. The outgoing request here is POST-with-body, so mureo's own app_secret / access_token are not in a URL — the exposure is whatever Graph chooses to put in an error body, written verbatim and unbounded to a file on disk.

It is also inconsistent with how the same Graph API is handled elsewhere in the same codebase. mureo/meta_ads/accounts.py scrubs str(exc) and breaks the exception chain with raise ... from None, specifically documenting that an httpx HTTPStatusError embeds the request URL in its __str__. refresh_meta_token_if_needed never received that treatment.

Note this is the same swallowed-failure path #578 was about: the refresh fails silently on every Meta call, and this log line is the only record. So the fix must keep it diagnosable — dropping the line entirely is not the answer.

Suggested direction

  • Stop interpolating resp.text into the exception message. Log resp.status_code plus a curated field from Graph's JSON error body (error.message / error.code / error_subcode), truncated, rather than the whole body.
  • Drop exc_info=True at auth.py:452 in favour of the message text now that it carries the useful part — matching the pattern applied across mureo/google_ads/ in google_ads logs SDK exceptions with exc_info=True, which prints request metadata #603 and used in mureo/amazon_ads/lwa.py and mureo/meta_ads/accounts.py.
  • Consider whether the AST regression guard added in google_ads logs SDK exceptions with exc_info=True, which prints request metadata #603 (tests/test_google_ads_log_exc_info.py, which sweeps mureo/google_ads/) should be widened to cover mureo/auth.py and the other credential-adjacent modules, so this class of problem cannot come back anywhere.

Provenance

Raised as a MEDIUM finding by the security review of PR #599 (the review that also surfaced #603's HIGH). The HIGH and the directory-permission MEDIUM were fixed in #599 / #604; this one was not, and is tracked here so it is not lost.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions