* fix(security): harden against command injection and sandbox escape
1. cli.ts: Remove auto-discover of arbitrary system binaries via denylist.
Unknown commands now require explicit registration via `opencli register`.
The previous denylist approach was trivially bypassable (bash, curl, etc.).
2. template.ts: Protect evalJsExpr against prototype chain escape.
Block expressions containing constructor/prototype/__proto__/process/etc.
Deep-copy context objects to sever prototype chains before passing to
new Function().
3. external.ts: Expand shell operator detection in parseCommand to cover
$(), $, #, \n, \r — preventing command substitution and comment injection.
4. fetch.ts: Use JSON.stringify for HTTP method in browser evaluate() instead
of raw string interpolation, preventing JS injection via crafted method values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: harden security-sensitive execution paths
* chore: tighten template sandbox guard
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Replace inline sync chrome.debugger.detach() in onUpdated listener
with the shared async detach() function for consistent cleanup behavior
across all detach paths.
- Only override navigator.plugins when empty (don't replace real user
browser plugins with fakes)
- Replace Error.prepareStackTrace (V8/Node-only) with
Error.prototype.stack getter override that works in browser context
- Fix \\n escaping in template literal for stack trace split/join
- Dynamic cdc_ variable scan via getOwnPropertyNames instead of
hardcoded names
- Update tests to cover 7 patches
Add stealth.ts module that patches browser globals to hide automation
fingerprints when opencli controls a browser via CDP or daemon extension.
Patches applied:
- navigator.webdriver → undefined (CDP sets it to true)
- window.chrome stub (only if missing)
- navigator.plugins fake list (only if empty)
- navigator.languages guarantee (only if empty)
- Permissions.query normalization for notifications
- Cleanup __playwright/__puppeteer/cdc_* artifacts
CDP mode: stealth registered via Page.addScriptToEvaluateOnNewDocument
(runs before any page JS on every navigation).
Daemon mode: stealth injected via exec after navigation, with guard
flag to prevent double-injection.
- Add getErrorMessage() to errors.ts (used in 5 files)
- Add DEFAULT_DAEMON_PORT to constants.ts (used in 5 files)
- Reduces code duplication and improves maintainability
* fix: remove duplicate getErrorMessage import in discovery.ts
Squash merge left a duplicate import line causing TS2300 and oxc parse
errors in CI. Also clean up stale blank lines in discovery.ts and
execution.ts.
* fix(e2e): broaden xiaoyuzhou skip logic for overseas CI runners
The isExpectedChineseSiteRestriction function only matched FETCH_ERROR
with specific HTTP status codes. On overseas CI runners, xiaoyuzhou may
also return PARSE_ERROR (mangled HTML) or NOT_FOUND (geo-redirected
pages), causing false test failures. Now matches all CliError codes
from the adapter.
* fix(external): replace execSync with execFileSync to prevent command injection
* fix(review): preserve Windows external installs and restore docs build
* fix(review): preserve Windows external installs after rebase
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(yollomi): add new commands and update documentation in README files
- Added yollomi commands for generating images, videos, and editing capabilities.
- Updated README.md and README.zh-CN.md to include yollomi in the command list.
- Enhanced SKILL.md with yollomi-related tags and usage examples.
* feat(yollomi): add yollomi adapter to documentation
- Included yollomi in the VitePress configuration for browser adapters.
- Updated adapters index documentation to reflect yollomi's capabilities and commands.
* fix(yollomi): bug fixes, tests & improvements
- models.ts: add browser: false (no browser connection needed for hardcoded data)
- edit.ts: remove unused resolveImageInput import
- upload.ts: lower video upload limit from 100MB to 20MB (base64 OOM risk)
- generate.ts: improve file extension detection using URL.pathname
- upscale.ts: use choices for scale arg, improve extension detection
- object-remover.ts: make image/mask args positional
- Add yollomi models tests to public-commands.test.ts
- Add yollomi generate/video graceful-failure tests to browser-auth.test.ts
---------
Co-authored-by: anichikage <hanzhishuai@bytedance.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(record): add live recording command for API capture
- Add `opencli record <url>` command that injects fetch/XHR interceptors
into all tabs in the automation window, polls captured requests, and
auto-generates YAML candidate adapters
- Support multi-tab recording: new tabs discovered during polling are
automatically injected
- Add --timeout (default 60s) for agent-friendly non-blocking operation;
stops on Enter, timeout, or SIGINT — whichever comes first
- Fix idempotent re-injection: restores original fetch/XHR before
re-patching so guard flag no longer blocks subsequent record runs
- Add --poll interval option (default 2000ms)
- Expand SKILL.md with full Record Workflow section: interceptor
internals, page-type capture expectations, YAML→TS conversion guide,
and troubleshooting table
* fix(record): fix XHR listener leak, pathChain syntax error, readline hang & args interpolation
- XHR send(): add __rec_listener_added guard to prevent duplicate event
listeners when XHR is reused (abort → open → send)
- pathChain: when findArrayPath returns '' (root-level array), data access
is just 'data' not 'data?.' which was invalid JS syntax
- waitForEnter(): return cleanup fn so timeout path can close readline.Interface
preventing the process from hanging on stdin after auto-timeout
- buildRecordedYaml: replace search/page query param values with template
vars ({{args.keyword}}, {{args.page}}) so generated YAML actually uses
the declared args instead of hardcoding the recorded URL
---------
Co-authored-by: yee.wang <yee.wang@lazada.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
- docs/adapters/browser/xiaohongshu.md: fill in search command description
(was empty), update usage examples with keyword positional arg
- TESTING.md: update unit test count 31→32 (search.test.ts added in #298),
add xiaohongshu/search.test.ts to the adapter test file list
* Add weibo search command
* fix(weibo/search): correct domain to weibo.com, add browser: true, fill doc description
- Change domain from s.weibo.com to weibo.com so browser cookies are picked
up correctly (matches hot.ts which also uses weibo.com)
- Add browser: true for consistency with other browser-based adapters
- Add description for weibo search in adapter docs table
---------
Co-authored-by: 小小机器人 <14351708+little-little-robot@user.noreply.gitee.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat(v2ex): add node, user, member, replies, nodes commands
Add 5 new public API commands to the v2ex adapter:
- node: browse topics by node name
- user: list topics by username
- member: show user profile
- replies: list topic replies
- nodes: list all nodes sorted by topic count
All commands use strategy: public, browser: false.
* test(v2ex): add E2E tests for node, user, member, replies, nodes commands
* docs(v2ex): update adapter docs with new commands
* fix(v2ex): address review findings - rate-limit guards, sort verification, docs
* docs(v2ex): update README command tables and add user example
* test(v2ex): improve test quality - soft guards, value assertions, smoke tests
- Replace isExpectedChineseSiteRestriction with if(code===0) soft guard
(V2EX is globally accessible; YAML fetch doesn't throw FETCH_ERROR)
- Add value assertions: member username===Livid, limit effectiveness
- Add smoke tests for node, member, replies, nodes commands
* fix(v2ex): add url field to node/user commands, add missing user smoke test
- Add url to node.yaml and user.yaml pipeline map steps and columns
(V2EX API provides item.url; improves usability for follow-up lookups)
- Add v2ex user smoke test (other 4 new commands all had smoke tests; user was missing)
- Update E2E assertions to verify url field in node/user results
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
Some environments (GUI apps, cron, IDE terminals) launch with a minimal
PATH that excludes standard directories like /usr/local/bin and /usr/sbin.
This causes external CLIs to fail when they try to run system commands
(e.g. sysctl).
Fix by ensuring standard system paths exist in process.env.PATH at
startup. This is a one-time fix that benefits ALL child processes —
isBinaryInstalled(), installExternalCli(), daemon spawn, etc. — without
needing per-call env patching.
Fixes#284
Co-authored-by: jackwener <jakevingoo@gmail.com>
* docs: add gws to External CLI Hub table in README
The Google Workspace CLI (gws) was registered in external-clis.yaml
but missing from the README table. Closes#120.
* docs: add gws to Chinese README External CLI Hub table
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
Narrow '#noteContainer img[src*="xhscdn"]' to
'#noteContainer .media-container img[src*="xhscdn"]'
to exclude user avatars and sidebar icons from downloads.
Closes#281
Expand HackerNews from 1 command to 8, covering all major HN use cases.
All YAML adapters, strategy: public, browser: false.
- new/best/ask/show/jobs: Firebase API list endpoints with deleted/dead filtering
- search: Algolia API with query + sort (relevance/date)
- user: Firebase user profile with date formatting
- top.yaml: add filter for deleted/dead items + dynamic pre-fetch limit
- E2E tests for all 7 new commands
- Update README, README.zh-CN, adapter docs
Co-authored-by: jackwener <jakevingoo@gmail.com>
turndown and @types/turndown were used in article-download.ts and
zhihu/download.test.ts but never declared in package.json, causing
CI failures on fresh npm ci installs.
Adds `opencli xiaohongshu publish` which automates posting a 图文 (image+text)
note via the creator center UI (creator.xiaohongshu.com/publish/publish).
Features:
- --title (required, max 20 chars)
- positional content argument
- --images comma-separated local file paths (jpg/png/gif/webp, max 9)
- --topics comma-separated hashtag names (without #)
- --draft flag to save as draft instead of publishing
Image upload uses DataTransfer injection into the file input element, converting
local files to base64 in Node.js and creating File blobs in the browser context.
Text fields use document.execCommand('insertText') for contenteditable editors.
Graceful debug screenshots on failure (/tmp/xhs_publish_*_debug.png).
Requires: opencli browser session logged into creator.xiaohongshu.com.
Replace fixed settleMs sleep in goto() with MutationObserver-based DOM
stability detection. The page is considered settled when no DOM mutations
occur for quietMs (default 500ms), with settleMs as a hard timeout cap.
Changes:
- Add waitForDomStableJs() shared helper to dom-helpers.ts
- Update Page.goto() and CDPPage.goto() to use smart settle
- No IPage interface changes (implementation detail only)
Key improvements over naive approach:
- Timer starts AFTER MutationObserver.observe() to avoid race condition
- Falls back to sleep(maxMs) if document.body is not available
- Monitors attributes in addition to childList/subtree
- quietMs defaults to 500ms (conservative) for async request buffering
- Add Origin header check: reject HTTP/WS from non chrome-extension:// origins
- Require X-OpenCLI custom header on all HTTP requests
- Remove Access-Control-Allow-Origin: * from all responses
- Add WebSocket verifyClient to reject malicious connections at upgrade
- Add 1MB body size limit to prevent OOM
- Update file header with security model documentation
Closes#268
- Remove setup command completely (no backward compat needed)
- Doctor now runs live connectivity test by default
- Add --no-live flag to skip if needed
- Update SKILL.md docs
- Delete setup.ts (fully redundant with doctor)
- opencli setup now prints deprecation warning and delegates to doctor
- doctor auto-starts daemon if not running (no more false 'not connected')
- Update all doc references (README, SKILL.md, docs/)