Skip to content

fix(security): keep WeCom callback msg_signature out of access logs - #40072

Closed
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/security-wecom-callback-signature-log-leak
Closed

briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/security-wecom-callback-signature-log-leak

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

This is a sibling follow-up to commit 514f502

  • What 514f502 covered: the BlueBubbles webhook adapter, whose auth secret (?password=) rides the inbound request target into aiohttp's default access log. It added access_log=None to the BlueBubbles web.AppRunner so the request target is never persisted to agent.log.
  • What 514f502 did NOT touch: the WeCom callback adapter (gateway/platforms/wecom_callback.py), whose msg_signature HMAC (plus timestamp/nonce, and the encrypted echostr on URL verification) rides the inbound request query string into the exact same aiohttp access log. Its web.AppRunner was still constructed with the default access logger enabled.
  • What this PR adds: access_log=None on the WeCom callback AppRunner, closing the identical query-string-borne-secret leak at the second platform that exhibits it.

What does this PR do?

The WeCom URL-verification handshake (_handle_verify) and every inbound callback (_handle_callback) read msg_signature, timestamp, nonce, and echostr from request.query. aiohttp's default access logger writes the full request target — including that query string — to agent.log verbatim, persisting the msg_signature HMAC (and the encrypted echostr) in plaintext logs. msg_signature is an HMAC-SHA1 over (token, timestamp, nonce, payload); a log reader who recovers a (msg_signature, timestamp, nonce, echostr) tuple can replay the URL-verification handshake and weaken callback-source authentication.

This disables the access log on the WeCom callback AppRunner (access_log=None) so the request target is never written, mirroring the BlueBubbles platform-side change in 514f502 exactly.

Note on scope: the central redaction denylist (agent/redact.py::_SENSITIVE_QUERY_PARAMS) is intentionally not changed. The request-target / URL query-param redaction path was deliberately turned OFF on main in 5f66c3647 fix(redact): pass web URLs through unchanged (#34029) — adding keys there would be dead code that contradicts that decision. Disabling the access log at the runner (the same defense 514f502 chose for BlueBubbles) is the live, correct fix.

Related Issue

N/A — sibling hardening follow-up to commit 514f502.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • 🔒 Security fix

Changes Made

  • gateway/platforms/wecom_callback.py: construct the callback web.AppRunner with access_log=None (mirrors BlueBubbles 514f502).
  • tests/gateway/test_wecom_callback.py: regression test asserting connect() builds the AppRunner with access_log=None.

How to Test

  1. uv run --with pytest --with pytest-xdist --with pytest-asyncio python3 -m pytest tests/gateway/test_wecom_callback.py -v
  2. Regression guard verified both directions: with the prod hunk stashed the new test fails (AppRunner must be constructed with an explicit access_log kwarg); restored, it passes. All 13 tests in the file pass.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix
  • I've run the focused tests and all pass
  • I've added tests for my changes
  • I've tested on my platform: macOS 15 (Darwin 25.4)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact (Windows, macOS) — log hygiene change, platform-independent

Escalation paths covered

The WeCom callback site exposed the query-string secret via aiohttp's access log. This PR closes that path at the runner. The redaction-denylist path is intentionally out of scope (request-target redaction is OFF on main per #34029); the access-log disable is the same defense the parent commit used for BlueBubbles.

The WeCom callback URL-verification handshake (_handle_verify) and every
inbound callback (_handle_callback) carry the msg_signature HMAC, plus
timestamp/nonce and (on verification) the encrypted echostr, in the
request query string. aiohttp's default access logger writes the full
request target — including that query string — to agent.log verbatim, so
the signature tuple is persisted in plaintext logs.

Construct the WeCom callback AppRunner with access_log=None so the request
target is never written to the access log, mirroring the BlueBubbles
webhook fix in 514f502, which disabled the access log on its own runner
for the same class of query-string-borne secret.
Copilot AI review requested due to automatic review settings June 5, 2026 19:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR prevents sensitive WeCom callback query-string parameters (notably msg_signature) from being written to logs by disabling aiohttp access logging for the WeCom callback server.

Changes:

  • Disable aiohttp access logging for the WeCom callback AppRunner by setting access_log=None.
  • Add an async test that asserts connect() constructs the AppRunner with access_log=None to avoid signature leakage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
gateway/platforms/wecom_callback.py Disables aiohttp access logging for the WeCom callback server to avoid leaking msg_signature in logs.
tests/gateway/test_wecom_callback.py Adds coverage ensuring connect() explicitly disables aiohttp access logging.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gateway/platforms/wecom_callback.py Outdated
Comment on lines +139 to +145
# The WeCom URL-verification handshake and every inbound callback
# carry the `msg_signature` HMAC (and `echostr`) in the request
# *query string*. aiohttp's default access logger would write that
# full request target to agent.log verbatim. Disable the access log
# so the signature is never persisted (mirrors the BlueBubbles
# webhook fix in 514f5020c).
self._runner = web.AppRunner(self._app, access_log=None)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — fixed in e2fc75e. Instead of access_log=None (which silenced all request logging incl. /health), connect() now installs a custom AbstractAccessLogger subclass via access_log_class. It keeps access logging on for observability and redacts the sensitive query values (msg_signature, echostr, timestamp, nonce) to REDACTED before they reach agent.log, preserving the path and any non-sensitive params. Added regression tests for the redaction helper, the logger output, and that connect() installs the redacting class with logging still enabled.

Comment thread gateway/platforms/wecom_callback.py Outdated
Comment on lines +143 to +144
# so the signature is never persisted (mirrors the BlueBubbles
# webhook fix in 514f5020c).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e2fc75e — dropped the raw commit-hash reference. The code comment now describes the leak and the redaction approach directly without pinning to a SHA.

Comment on lines +313 to +320
"""The WeCom URL-verification handshake and inbound callbacks carry the
``msg_signature`` HMAC (and ``echostr``) in the request *query string*
(see ``_handle_verify`` / ``_handle_callback``). aiohttp's default access
logger would write that full request target to agent.log verbatim, leaking
the signature. ``connect()`` must build the AppRunner with
``access_log=None`` so it is never persisted -- mirroring the BlueBubbles
webhook fix in 514f5020c.
"""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e2fc75e — the test docstring no longer references a commit SHA; it describes the rationale (sensitive query params must not be persisted to access logs) directly.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/wecom WeCom / WeChat Work adapter labels Jun 5, 2026
…ure instead of disabling access logs

Replace access_log=None with a redacting AbstractAccessLogger subclass so
request access logging stays enabled (preserving /health and traffic/latency
observability) while the msg_signature HMAC, echostr, timestamp and nonce
query values are replaced with REDACTED before they reach agent.log.

Drop the brittle raw-commit-hash references in the code comment and test
docstring in favor of describing the prior fix directly.

Add regression tests for the redaction helper, the access-logger output, and
that connect() installs the redacting class with access logging still on.
@briandevans

Copy link
Copy Markdown
Contributor Author

Closing to keep the hardening queue focused — this is a mechanical sibling-widen of the 514f502 access-log family (mirrors access_log=None to the WeCom callback runner) that's sat 31d with no maintainer review. Happy to reopen or fold it into a broader access-log-hardening pass across the remaining aiohttp runners if that's preferred.

@briandevans briandevans closed this Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/wecom WeCom / WeChat Work adapter type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants