Repository navigation
fix(wechat): a scalar allowed_file_extensions config silently widens back to all 37 defaults - #6737
Conversation
willem-bd
left a comment
There was a problem hiding this comment.
Reviewed against head 168d0ef. The core fix is correct and well-tested: I ran tests/test_wechat_allowed_file_extensions.py (7 passed) and tests/test_wechat_channel.py (95 passed total) against this head, and the scalar/typing/empty-list behaviors now match the docstring and PR description. One remaining silent-misconfiguration gap in the new scalar path, suggested below.
There was a problem hiding this comment.
This scalar path has the same trap its sibling _parse_allowed_users already guards against (it warns when a scalar contains separators). A natural YAML habit like allowed_file_extensions: ".pdf, .zip" now becomes the single bogus entry {".pdf, .zip"}, which matches no filename suffix — verified on this head that even report.pdf is then denied, with no startup log pointing at the config. That is the same invisible-posture-change class this PR fixes, just in the fail-closed direction: every inbound/outbound file gets blocked until the operator happens to notice the per-file "blocked, skipping" warnings. Consider mirroring _parse_allowed_users here: warn when the scalar string contains , or whitespace (or split on commas), so the misconfiguration surfaces at startup.
There was a problem hiding this comment.
Pushed in 9545d80. _coerce_str_set now mirrors the _parse_allowed_users guard: a scalar string containing , or internal whitespace logs
[WeChat] allowed_file_extensions is a scalar string containing separators; treating it as one literal extension. Use a YAML list for multiple extensions
at construction time and the value stays fail-closed ({".pdf, .zip"} still denies report.pdf), so the posture change is visible at startup instead of only through the per-file "blocked, skipping" warnings.
I kept the one-literal semantics rather than splitting on commas: silently reinterpreting the value would make the config mean something the operator did not write, and the sibling guard already sets the warn-don't-split precedent.
Two tests added (test_scalar_extension_config_with_separators_warns, test_space_separated_scalar_extension_config_warns). Measured before the guard existed: 2 failed / 7 passed, both failures on the missing warning. After: 9 passed, ruff check and ruff format --check clean, and -k wechat is 213 passed / 8 skipped. Also normalized the three [wechat] log prefixes inside this function to [WeChat] to match the rest of the file.
…list A comma- or space-separated scalar string is still one literal extension, so `allowed_file_extensions: ".pdf, .zip"` blocks every file with nothing in the startup log pointing at the config. Mirror the `_parse_allowed_users` guard.

Fixes #6736
Why
While reading the WeCom channel's inbound file gate I noticed that
allowed_file_extensionscan be configured and silently do nothing. Writing it as a scalar, which is the natural way to express a one-item restriction in YAML:makes
_coerce_str_setfall intoif not isinstance(value, (list, tuple, set, frozenset)): return set(default)— a string is not one of those containers — so the channel ends up admitting all 37 built-in extensions instead of only.pdf. There is no log line saying the configured value was unreadable, so the operator has no way to see that the restriction they wrote was dropped.The pain is the direction of the failure. This set gates which files the Gateway may read; a restriction that widens instead of narrowing is a posture change that is invisible in the logs.
What changed
allowed_file_extensions: ".pdf"now means exactly that — a bare string is the single entry it names, matching what the sibling_coerce_host_suffixesalready accepts one method below..123that no real file can ever match.Behaviour for a well-formed list config is unchanged, and an unset
allowed_file_extensionsstill resolves to the same 37 defaults.Surface area
backend/appfrontend/langgraph.json, or prompt changedocker/or sandboxed executionskills/backend/pyproject.tomlorfrontend/package.json(say what it buys us)Only an operator who configured
allowed_file_extensionsas a scalar or with non-string entries notices a difference — and for them the change is that their configured restriction starts applying.Bug fix verification
backend/tests/test_wechat_allowed_file_extensions.pymainand green on this branch? yesOn
main(c89fed0b) with the fix reverted:test_scalar_extension_config_narrows_the_allowlist—.pdfproduced 37 extensionstest_extension_config_of_the_wrong_type_is_logged_before_falling_back— no log emittedtest_unreadable_extension_entry_is_skipped_and_logged—123became a.123memberOn this branch:
Validation
From
backend/on Windows, using the repo's own venv (makeis not available in this environment, so the underlying commands were run directly):The 2 skips are pre-existing in
tests/test_wechat_channel.pyon this host, not introduced here.AI assistance
Tool(s) used: Qoder
How you used it: Qoder located the coercions and drafted the tests against the observed config shapes; the failing-then-passing test file and the guard were re-run and verified locally, and the accepted shape was matched to the in-file precedent (
_coerce_host_suffixes) and toa97772d0for WeCom'sallowed_users.