4 Commits

Author SHA1 Message Date
Zhengxu (Joshua) Jin
70e8f8974f
fix(channels): parse Slack and DingTalk allowed_users fail-closed (#6742)
* fix(channels): parse Slack and DingTalk allowed_users fail-closed

Slack and DingTalk still built their allowlists with their own
_normalize_allowed_users: every entry went through str() without
stripping, only list/tuple/set counted as collections, and the gate was
`if self._allowed_users and ...`. A configured list whose entries are all
blank, such as `allowed_users: [$SLACK_USER_ID]` with the variable set to
an empty string, became an empty set and let every sender through.
" U123 " never matched U123, and a frozenset was stringified into one ID.

Parse both with the shared parse_allowed_users helper that Telegram,
WeChat and QQ use, and gate on `is not None`: unset, null, empty
collections and a blank string allow everyone; a scalar is one entry;
strings are stripped and integers keep matching their string form, as
before; booleans, floats, None and mappings are rejected (a mapping as a
whole); a configured value with no valid ID denies every sender and logs
an error. A scalar containing commas or whitespace warns that it stays
one literal ID.

* refactor(channels): share the Slack and DingTalk allowlist coercer and separator warning

Slack and DingTalk had byte-identical ID coercers and their own copies of
the scalar-separator warning. Move the coercer into allowed_users.py as
parse_string_or_int_user_id and opt both channels into
warn_on_scalar_separators, as QQ and WeChat do. Log messages are unchanged.

Pin that a separator-only string such as "," stays one literal ID
(denying everyone else) and still warns.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-14 10:56:31 +08:00
Grapette.L
9b2151a3a8
fix(buzz): a single mention_free_channels ID was split into characters, a numeric one stopped the connector (#6776)
* fix(buzz): accept a single mention-free channel id

`_mention_free` iterated the configured value directly, so one unquoted
UUID became a set of its characters and a numeric or boolean scalar
raised TypeError while the channel was being constructed. Both shapes are
documented as valid for the sibling Discord connector.

Parse the key through a dedicated helper that follows the Discord
convention: a scalar is a single entry, list entries are kept verbatim,
and an unusable entry never matches so `require_mention` stays in force.

* fix(channels): share scalar-or-list channel ID parsing

* docs(channels): keep guidance within CI size budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-13 14:55:49 +08:00
Zhengxu (Joshua) Jin
47b6755ce9
fix(qq): parse allowed_users with the shared fail-closed allowlist (#6730)
* fix(qq): parse allowed_users with the shared fail-closed allowlist

QQChannel built its allowlist with set(config.get("allowed_users") or []).
A single OpenID, which is what an `allowed_users: $QQ_ALLOWED_USERS`
reference resolves to, became a set of its characters, so the configured
user was silently dropped. A mapping authorized every key, including one
mapped to false, and a numeric scalar raised TypeError while the channel
was being constructed.

Parse it with the shared parse_allowed_users helper that Telegram and
WeChat already use: unset, null, empty collections and a blank string
allow everyone; a scalar OpenID is one entry; entries that are not
non-blank strings (such as numeric QQ account numbers) are dropped with a
warning, and a mapping is rejected as a whole; a configured value with no
valid OpenID denies every sender and logs an error. A scalar containing commas or whitespace warns
that it stays one literal ID. Binding codes still precede the allowlist.

* test(qq): gate group messages by member OpenID in allowlist tests

Every allowlist test sent a C2C frame, and the shared event() helper puts
the sender in both user_openid and member_openid, so a group-path
regression in sender resolution would pass unnoticed. admits() now takes
a kind; group frames carry only member_openid and a group_openid that
differs from the sender. The listed-OpenID test runs for both kinds, and a
new test shows that listing the group's OpenID does not admit its members.

Document that a blank or whitespace-only allowed_users string allows every
sender, like an unset value or an empty list.

* refactor(channels): share scalar allowlist separator warnings

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-12 22:19:11 +08:00
Shxiao
a97772d016
fix(wechat): deny all users when allowed_users has no valid ID (#6376)
* fix(wechat): deny all users when allowed_users has no valid ID

A single iLink user ID was split into characters, and a list with no
usable ID collapsed to an empty set that the inbound gate read as
allow-all.

* fix(wechat): fail closed on numeric and mapping allowed_users

Integer-valued floats now match the sender's integer text. Fractions,
non-finite numbers, and mappings no longer admit anyone, and a denied
sender is dropped before inbound media is downloaded.

* docs(channels): keep WeChat guidance within size budget

* fix(wechat): warn on scalar allowlist separators

* refactor(channels): share fail-closed allowlist parsing

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-06 21:17:11 +08:00