Skip to content

fix(channels/wechat-sidecar): recover context_token via librefang_user across sidecar restart#5448

Merged
houko merged 1 commit into
mainfrom
fix/wechat-sidecar-restart-context-token
May 21, 2026
Merged

fix(channels/wechat-sidecar): recover context_token via librefang_user across sidecar restart#5448
houko merged 1 commit into
mainfrom
fix/wechat-sidecar-restart-context-token

Conversation

@houko

@houko houko commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Runtime hotfix surfaced by the #5439 follow-up audit chain.

WeChat sidecar's in-process `_user_context_tokens[user_id]` cache vanishes on sidecar restart (any operator upgrade, debug pass, or supervisor respawn). Without the cached `context_token`, the bot's first reply after restart posts with an empty token — iLink may reject it or post out-of-thread.

Fix

Mirror the dingtalk #5423 / qq #5431 / omnibus #5439 pattern:

  • `parse_wechat_msg` also stashes `context_token` in `ChannelUser.librefang_user`, which the bridge round-trips bytewise (verified at `crates/librefang-channels/src/sidecar.rs:766` inbound, `:1204` outbound).
  • `on_send` keeps the in-memory cache as the primary path (freshness invariant — the latest token Lark/iLink issued is more current than whichever inbound the daemon happens to round-trip back). Falls back to `librefang_user` when the cache is empty (post-restart scenario).
  • HTTP-scheme / whitespace / `@` guard rejects cross-channel pollution (dingtalk URL, telegram `@username`, etc.).

Tests

  • New `test_on_send_recovers_context_token_from_user_librefang_user_when_cache_cold` simulates post-restart (cache empty, `librefang_user` carries the token), asserts on_send recovers it.
  • New `test_on_send_ignores_url_shaped_librefang_user` pins the cross-channel pollution guard.
  • `cd sdk/python && pytest tests/test_wechat_adapter.py` — 63 passed (was 61; +2 regression guards).

Cross-reference

…r across sidecar restart

The in-process `_user_context_tokens[user_id]` cache populated by
inbound iLink callbacks vanishes on sidecar restart (any operator
upgrade, debug pass, or supervisor respawn). Without the
context_token, the bot's first reply after restart posts with an
empty token — iLink may reject it or post out-of-thread.

Fix: parse_wechat_msg also surfaces `context_token` via
`ChannelUser.librefang_user`, which the bridge round-trips bytewise
through serde (verified at crates/librefang-channels/src/sidecar.rs:766
inbound, :1204 outbound) so it survives serde + restart cleanly. The
in-memory cache stays as the freshness signal (the latest token Lark/
iLink issued is more current than whichever inbound the daemon happens
to round-trip back) and takes precedence in on_send.

Tests:
- New test_on_send_recovers_context_token_from_user_librefang_user_when_cache_cold
  simulates post-restart (cache empty, cmd.user.librefang_user carries
  the token) and asserts on_send recovers it.
- New test_on_send_ignores_url_shaped_librefang_user pins the cross-
  channel pollution guard (librefang_user is shared across channels —
  dingtalk puts a sessionWebhook URL there, telegram puts @username).
- cd sdk/python && pytest tests/test_wechat_adapter.py — 63 passed
  (was 61; +2 regression guards).

Surfaces from the #5439-followup audit chain (dingtalk #5423 →
qq #5431 → omnibus #5439 → THIS). Same library_user routing pattern,
distinct symptom — restart-survivability rather than cap-gate stripping.
@houko
houko merged commit 2baf830 into main May 21, 2026
15 checks passed
@houko
houko deleted the fix/wechat-sidecar-restart-context-token branch May 21, 2026 02:04
@github-actions github-actions Bot added area/sdk JavaScript and Python SDKs size/M 50-249 lines changed labels May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sdk JavaScript and Python SDKs size/M 50-249 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant