Skip to content

fix(v2ex): percent-encode caller values in API query strings - #565

Closed
SEPURI-SAI-KRISHNA wants to merge 1 commit into
Panniantong:mainfrom
SEPURI-SAI-KRISHNA:fix/v2ex-encode-query-params
Closed

fix(v2ex): percent-encode caller values in API query strings#565
SEPURI-SAI-KRISHNA wants to merge 1 commit into
Panniantong:mainfrom
SEPURI-SAI-KRISHNA:fix/v2ex-encode-query-params

Conversation

@SEPURI-SAI-KRISHNA

Copy link
Copy Markdown
Contributor

Problem

agent_reach/channels/v2ex.py interpolates caller-supplied values straight into query strings:

f"https://www.v2ex.com/api/topics/show.json?node_name={node_name}&page=1"
f"https://www.v2ex.com/api/members/show.json?username={username}"
f"https://www.v2ex.com/api/replies/show.json?topic_id={topic_id}&page=1"

Nothing is percent-encoded, so five separate things go wrong. Reproduced on main by stubbing _get_json and parsing what would have been sent:

Input URL built Server actually receives
node_name="python&page=99" ?node_name=python&page=99&page=1 page twice — pagination silently hijacked
node_name="foo#bar" ?node_name=foo#bar&page=1 node_name=foo, page never sent — it lands in the fragment
node_name="c++" ?node_name=c++&page=1 node_name="c "+ decodes as space
node_name="hello world" ?node_name=hello world&page=1 raw space in the request line
username="张三" UnicodeEncodeError: 'ascii' codec can't encode characters

The last one is the worst: any non-ASCII value raises UnicodeEncodeError straight out of urllib — an unhandled crash rather than a channel error. That's easy to hit on a Chinese-language site from a Chinese-first project.

from agent_reach.channels.v2ex import V2EXChannel
V2EXChannel().get_user("张三")
# UnicodeEncodeError: 'ascii' codec can't encode characters in position 36-37

These values reach the channel from agents and from parsed URLs, so they are not guaranteed to be tidy slugs.

The same raw interpolation is in two returned display URLs (/member/<username>, /t/<topic_id>) and in the search advisory URL that search() hands the user to click.

Fix

One _api_url() helper that builds every V2EX API URL through urllib.parse.urlencode, plus urllib.parse.quote on the two path-segment fallbacks and the search advisory link.

After the change every value round-trips exactly through parse_qs, page=1 always survives, and the non-ASCII call reaches the server and returns a normal HTTP 404 instead of crashing.

get_topic is typed topic_id: int but nothing enforces it; routing it through urlencode means a stray string can no longer truncate the query, while plain ints serialise unchanged (id=123).

Tests

15 new cases in tests/test_v2ex_channel.py, following the existing module-level style and stubbing _get_json so nothing touches the network:

  • test_get_node_topics_percent_encodes_node_name / test_get_user_percent_encodes_username — parametrised over all five hostile values, asserting the value round-trips and no fragment appears
  • test_get_topic_percent_encodes_ids_in_both_requests / test_get_topic_replies_request_keeps_page_parameter — both requests get_topic makes
  • test_get_topic_accepts_plain_int_unchanged — regression guard for the normal path
  • test_search_advisory_url_is_encoded, test_fallback_display_urls_are_encoded

pytest -q: 457 passed (was 442). ruff check adds no new findings.

Scope

Confined to URL construction. It does not touch _get_json, so it does not overlap the TLS-EOF transport work in #531 or #536 — both of those rewrite only the fetch layer and neither encodes query values. This should merge cleanly alongside either.

@Panniantong

Copy link
Copy Markdown
Owner

感谢贡献。该修复已在 #578 以保留你作者身份的提交 544e0e7 重实现并合并到 main;保留现有 TLS EOF 安全回退、白名单和响应上限,并补齐空格、Unicode、&、#、+ 及 fallback 路径编码测试。全量 CI 已通过,因此关闭此旧 PR。

@Panniantong Panniantong closed this Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants