Skip to content
Report library
Purpose / Browser automation

Opencli Browser Skill Security Audit

What the author says it does (original text)

Use when an agent needs to drive a real Chrome window via opencli — inspect a page, fill forms, click through logged-in flows, or extract data ad-hoc. Covers the selector-first target contract, compound form fields, stale-ref handling, network capture, and the agent-native envelopes the CLI returns. Not for writing adapters — see opencli-adapter-author for that.

Independent security check

Do not install or run it yet

Files checked
1
Risks found
6
Could it run dangerous commands?Looks for programs run straight after downloading, remote control of your computer, and hidden commands.No risks found
Could it expose your files or keys?Looks for uploads of files containing passwords or keys, and keys written directly in the code.Risks found: 3
High risk

Full API responses from authenticated pages are persisted in a network cache

Source references: 3
What we found

The Skill favors network capture, can output individual or all full response bodies, and stores captures under `~/.opencli/cache/browser-network/` with a default 24-hour TTL. Responses may contain profiles, messages, session tokens, or business data.

Why this matters

Sensitive responses can appear both in agent output and on disk, where other processes, backups, or later sessions may read them.

The Skill recommends capturing page APIs, permits retrieval of individual or all full response bodies, and stores captured entries under the user's home directory with a default 24-hour TTL. When used in an authenticated tab, those bodies may contain account or business data. The source does not prove session tokens are captured or describe cache permissions, encryption, or cleanup, but persistent storage of sensitive responses is a plausible risk. Users can restrict `network --raw`, shorten TTL, and ask how the cache is protected and deleted.

SKILL.md:71In the instructionsOpen original file
9. **`eval` is read-only.** Wrap the JS in an IIFE and return JSON. If you need to *change* the page, use the structured `click` / `type` / `select` / `keys` commands instead — they produce structured output and fingerprints, `eval` does not.10. **Prefer `network` to screen-scraping.** If a page you care about fetches its data from a JSON API, the API is almost always more reliable than scraping the rendered DOM. Capture once, inspect the shape, then `--detail <key>` the body you need.
Show 2 other places
SKILL.md:199In the instructionsOpen original file
```bashbrowser network                        # shape preview + cache key listbrowser network --detail <key>         # full body for one cached entrybrowser network --filter "field1,field2"  # keep only entries whose body shape contains ALL fields as path segmentsbrowser network --all                  # include static resources (usually noise)browser network --raw                  # full bodies inline — large; use sparinglybrowser network --ttl <ms>             # cache TTL (default 24h)```List entries look like `{key, method, status, url, ct, size, shape, body_truncated?}`. Detail envelope is `{key, url, method, status, ct, size, shape, body, body_truncated?, body_full_size?, body_truncation_reason}`. Cache lives in `~/.opencli/cache/browser-network/` so you can re-inspect without re-triggering the request.
SKILL.md:207In the instructionsOpen original file
List entries look like `{key, method, status, url, ct, size, shape, body_truncated?}`. Detail envelope is `{key, url, method, status, ct, size, shape, body, body_truncated?, body_full_size?, body_truncation_reason}`. Cache lives in `~/.opencli/cache/browser-network/` so you can re-inspect without re-triggering the request.
High risk

Examples place login passwords or payment-field values into commands and output

Source references: 4
What we found

The login recipe supplies a password as a plaintext `type` argument. The cross-origin iframe recipe reads a card-number field, while string results from `eval` are written directly to stdout. With real secrets, these values may enter agent transcripts, command records, process arguments, or logs.

Why this matters

Account passwords or payment information may be exposed to the agent service, log viewers, or other processes on the same device.

The login recipe is an example using a fictional site, email, and password, so it does not itself leak real credentials; however, it models placing a password in a command argument. Another recipe reads a card-number value from a cross-origin payment iframe, while the reference says string results go directly to stdout. Applying these patterns to real secrets could expose them in process arguments, terminal output, or logs. Users can require that sensitive values never appear in command lines or `eval` output and use non-echoing input mechanisms.

SKILL.md:193In the instructionsOpen original file
- **`web read --url <url>`** — One-shot Markdown reader for arbitrary pages. It expands relevant same-origin iframes by default, so old iframe-shell sites work better than with a top-document-only scrape. Use `--frames all-same-origin` when completeness matters more than Markdown noise. For AJAX shell pages use `opencli web read --url <url> --wait-for "<selector>" --wait-until networkidle --diagnose`; diagnostics show frame URLs, empty containers, and API-like XHRs. If the value you need is table/API data, switch to `browser network` or a dedicated adapter instead of relying on Markdown.- **`browser eval <js> [--frame N]`** — Run an expression in the page (or in a cross-origin frame via `--frame`). Wrap in an IIFE and return JSON. Read-only: no `document.forms[0].submit()`, no clicks, no navigations. If the result is a string, stdout is the raw string; otherwise it's JSON.- **`browser extract [--selector <css>] [--chunk-size N] [--start N]`** — Markdown extraction of long-form content with a continuation cursor. Returns `{url, title, selector, total_chars, chunk_size, start, end, next_start_char, content}`. Loop on `next_start_char` until it is `null`. Auto-scopes to `<main>`/`<article>`/`<body>` if you don't pass `--selector`.
Show 3 other places
SKILL.md:326In the instructionsOpen original file
```bashopencli browser login open "https://example.com/login"opencli browser login state                          # find [N] for email, password, submitopencli browser login type 4 "me@example.com"opencli browser login type 5 "hunter2"opencli browser login get value 4                    # verify (autocomplete can eat chars)opencli browser login click 6                        # submitopencli browser login wait selector "[data-testid=account-menu]" --timeout 15000
SKILL.md:399In the instructionsOpen original file
```bashopencli browser checkout frames# -> [{"index": 0, "url": "https://checkout.stripe.com/...", ...}]opencli browser checkout eval "(() => document.querySelector('input[name=cardnumber]')?.value)()" --frame 0```
SKILL.md:325In the instructionsOpen original file
```bashopencli browser login open "https://example.com/login"opencli browser login state                          # find [N] for email, password, submitopencli browser login type 4 "me@example.com"opencli browser login type 5 "hunter2"opencli browser login get value 4                    # verify (autocomplete can eat chars)opencli browser login click 6                        # submitopencli browser login wait selector "[data-testid=account-menu]" --timeout 15000
Medium risk

The upload command can transfer local files directly to a website

Source references: 2
What we found

`browser upload` accepts local paths and attaches files through CDP. Although the instructions say not to invent paths and to respect accepted types, they do not require showing the full path, destination site, and file list for renewed user confirmation.

Why this matters

A mistaken path or target control could upload private documents, images, or other local material to a third-party site or account.

The upload command uses CDP to attach specified local paths to a webpage file input, which is a real disclosure of user files. The document says not to invent paths and to respect `accept`, but it does not require checking the destination domain, full paths, and file list or obtaining separate confirmation. If the agent selects the wrong page, control, or file, content could be handed to the wrong site. Users can require a destination-and-file preview and permit only explicitly named paths.

SKILL.md:165In the instructionsOpen original file
| `browser uncheck [target] [--role R --name N] [--nth N]` | Ensures checkbox/aria-checked control is unchecked. Radio buttons cannot be unchecked directly; select another radio in the group instead. || `browser upload [target] <file...> [--role R --name N] [--nth N]` | Attaches local file path(s) to an `input[type=file]` via CDP. With semantic flags, omit `target` and pass files as positionals. Returns `{uploaded, files, file_names, target, matches_n, match_level, multiple?, accept?}`. || `browser drag [source] [target] [--from-role R --from-name N] [--to-role R --to-name N] [--from-nth N] [--to-nth N]` | Mouse-based drag from one resolved element center to another. Works for mouse-listener drag libraries; native HTML5 `dataTransfer` drops may need a site-specific fallback. Returns `{dragged, source, target, source_matches_n, target_matches_n, ...}`. |
Show 1 other places
SKILL.md:272In the instructionsOpen original file
Do not invent file paths. Upload is done via the normal click flow — respect `accept` when telling the user what to upload.
Could it delete files or keep running?Looks for broad file deletion, disk overwrites, and programs set to start automatically.No risks found
Could it bypass safety checks?Looks for skipped website security checks, excessive file access, or actions that skip your approval.Risks found: 3
High risk

Binding is treated as persistent control authorization over a logged-in tab

Source references: 4
What we found

The instructions interpret one `bind` as explicit agent ownership of the tab, permit navigation, and impose no idle timeout. A limited task can therefore leave control active until manual unbinding, window closure, or daemon restart.

Why this matters

If the agent continues or a later task reuses the session, it can browse, fill, or click within the user's authenticated account and may alter account data or trigger actions that were not separately confirmed.

The Skill explicitly supports binding a logged-in or SSO tab and treats that binding as “explicit agent ownership” permitting navigation. There is no idle-close timer; control persists until unbind, tab/window closure, or daemon restart. If the user intended only a short task and the agent does not unbind, later control of that authenticated tab remains possible. Users can require `unbind` after every task and limit permitted sites and actions while bound.

SKILL.md:30In the instructionsOpen original file
- Owned browser sessions keep a tab lease alive between calls. Release it with `opencli browser <session> close` or let the idle timeout expire.- `opencli browser <session> bind` binds the Chrome tab you already have open to that session. Use this for logged-in pages, SSO flows, or pages you manually positioned before handing control to the agent.- `--window foreground|background` (or `OPENCLI_WINDOW=foreground|background`) chooses whether OpenCLI creates/focuses a foreground browser window or uses a background browser window for owned sessions.
Show 3 other places
SKILL.md:45In the instructionsOpen original file
Navigation is allowed on bound sessions because the session now represents explicit agent ownership of that tab. Tab mutation (`tab new`, `tab select`, `tab close`) is still blocked for bound sessions. Use an owned session when you want OpenCLI to manage tab lifecycle.Bound sessions have no OpenCLI idle-close timer; the binding lasts until `unbind`, tab close, window close, or daemon restart.
SKILL.md:43In the instructionsOpen original file
Binding never owns the user window and never closes the user tab. It fails closed if the tab is closed or becomes non-debuggable. Re-run `opencli browser <session> bind` when you switch to a different real tab.Navigation is allowed on bound sessions because the session now represents explicit agent ownership of that tab. Tab mutation (`tab new`, `tab select`, `tab close`) is still blocked for bound sessions. Use an owned session when you want OpenCLI to manage tab lifecycle.
SKILL.md:47In the instructionsOpen original file
Bound sessions have no OpenCLI idle-close timer; the binding lasts until `unbind`, tab close, window close, or daemon restart.
High risk

Troubleshooting asks users to enable remote debugging or disable password-manager extensions

Source references: 2
What we found

The Skill recommends launching Chrome with a remote debugging port and temporarily disabling 1Password or other CDP-using extensions when attachment fails. This can weaken existing browser isolation or credential protections.

Why this matters

While the debugging endpoint is reachable or protective extensions are disabled, logged-in pages, cookies, form contents, and browser actions may be easier for other local processes to observe or control.

Troubleshooting does recommend starting Chrome with a remote-debugging port or temporarily disabling 1Password and similar extensions. Both change the browser's security posture: a debugging interface may broaden control, while disabling a password manager temporarily removes its protections or warnings. The source does not state the port's bind scope, access controls, or a restoration step, so severity depends on the OpenCLI/Chrome setup. Users can require an isolated browser profile, localhost-only debugging, and closure/restoration afterward.

SKILL.md:21In the instructionsOpen original file
Until `doctor` is green, nothing else will work. Typical failures: Chrome not running, extension not installed, debug port blocked by 1Password / other extensions. The doctor output tells you which.
Show 1 other places
SKILL.md:428In the instructionsOpen original file
|---------|-----|| `opencli doctor` red: "Browser not connected" | Start Chrome with `--remote-debugging-port=9222`, or install the extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/opencli/ildkmabpimmkaediidaifkhjpohdnifk). || `attach failed: chrome-extension://...` | Disable 1Password / other CDP-hungry extensions temporarily. || `selector_not_found` right after `state` | Page mutated. `wait selector "..."` then retry. |
Medium risk

Declared file permissions exceed what browser operation requires

Source references: 4
What we found

In addition to Bash restricted to `opencli`, the metadata permits general Read, Edit, and Write tools. The stated body purpose is browser control and does not define a corresponding need or boundary for arbitrary editing or writing of user files.

Why this matters

If the host enforces this declaration as permissions, an agent running the Skill could read or persistently modify accessible files unrelated to the browser task. The upload feature could also use Read access to obtain those files.

The metadata actively grants general `Read`, `Edit`, and `Write` capabilities, while the stated core purpose is browser control through restricted `opencli` Bash commands. Some documented features may need access to upload paths or screenshot/download outputs, but no text explains why unrestricted editing across unspecified paths or file types is necessary. If the host enforces this declaration as permission, the agent could alter files unrelated to the browser task. Users can ask the author to remove `Edit` and scope reads/writes to explicit upload and output directories.

SKILL.md:3In the instructionsOpen original file
name: opencli-browserdescription: Use when an agent needs to drive a real Chrome window via opencli — inspect a page, fill forms, click through logged-in flows, or extract data ad-hoc. Covers the selector-first target contract, compound form fields, stale-ref handling, network capture, and the agent-native envelopes the CLI returns. Not for writing adapters — see opencli-adapter-author for that.allowed-tools: Bash(opencli:*), Read, Edit, Write---
Show 3 other places
SKILL.md:11In the instructionsOpen original file
This skill is for **driving a live browser** to accomplish an agent task. If you are building a reusable adapter under `~/.opencli/clis/<site>/` use `opencli-adapter-author` instead.
SKILL.md:139In the instructionsOpen original file
| `browser frames` | List cross-origin iframe targets. Pass the index to `--frame` on `eval`. || `browser screenshot [path]` | Viewport PNG. No path → base64 to stdout. Prefer `state` when you just need structure. || `browser screenshot --annotate [path]` | Visual ref map. Refreshes DOM refs and overlays visible `[N]` labels so the screenshot maps back to `browser click <ref>` targets. Use for icon-only controls, visual layouts, charts, or when text state is ambiguous. |
SKILL.md:165In the instructionsOpen original file
| `browser uncheck [target] [--role R --name N] [--nth N]` | Ensures checkbox/aria-checked control is unchecked. Radio buttons cannot be unchecked directly; select another radio in the group instead. || `browser upload [target] <file...> [--role R --name N] [--nth N]` | Attaches local file path(s) to an `input[type=file]` via CDP. With semantic flags, omit `target` and pass files as positionals. Returns `{uploaded, files, file_names, target, matches_n, match_level, multiple?, accept?}`. || `browser drag [source] [target] [--from-role R --from-name N] [--to-role R --to-name N] [--from-nth N] [--to-nth N]` | Mouse-based drag from one resolved element center to another. Works for mouse-listener drag libraries; native HTML5 `dataTransfer` drops may need a site-specific fallback. Returns `{dragged, source, target, source_matches_n, target_matches_n, ...}`. |
Could it mislead the AI or hide text?Checks the skill instructions for requests to ignore you, influence the report, or hide text in invisible characters.No risks found
Could it change links or payment recipients without asking?Looks for forced referral or payment changes combined with instructions to hide the change.No risks found

Inside this skill

8 instruction sections

This Skill uses OpenCLI to control a real Chrome browser, including inspection, typing, clicking, file uploads, and data extraction on logged-in pages.

View source
SKILL.md:3In the instructionsOpen original file
name: opencli-browserdescription: Use when an agent needs to drive a real Chrome window via opencli — inspect a page, fill forms, click through logged-in flows, or extract data ad-hoc. Covers the selector-first target contract, compound form fields, stale-ref handling, network capture, and the agent-native envelopes the CLI returns. Not for writing adapters — see opencli-adapter-author for that.allowed-tools: Bash(opencli:*), Read, Edit, Write---

It requires inspecting the page before interaction, checking actual values after typing or selection, and refreshing state after navigation.

View source
SKILL.md:62In the instructionsOpen original file
1. **Always inspect before you act.** Run `state` or `find` first. Never hard-code a ref or selector from memory across sessions — indices are per-snapshot.2. **Prefer site adapters before raw browser driving.** If `opencli <site> <command>` already covers the task, use that adapter command first (`opencli facebook notifications`, `opencli reddit read`, `opencli chatgpt model <level>`, etc.). Use `opencli browser ...` only for gaps, debugging, or one-off UI flows the adapter does not expose.3. **Prefer numeric ref over CSS once you have it.** Numeric refs survive mild DOM shifts because the CLI fingerprints each tagged element. A CSS selector written by hand will break the first time the site re-renders.4. **Read `match_level` after every write.** `exact` = all good. `stable` = the element is the same but some soft attrs drifted — your action still applied. `reidentified` = the original ref was gone and the CLI found a unique replacement; double-check you hit the right element.5. **Use the `compound` field for form controls.** Do not regex-guess a date format, do not `state` twice to get the full `<select>` options list. The compound envelope has the format string, full option list up to 50, `options_total` for overflow, and `accept`/`multiple` for `<input type=file>`.6. **Verify writes that matter.** After `type <target> <text>`, run `get value <target>`. After `select`, run `get value`. Autocomplete widgets, React controlled inputs, and masked fields all silently eat characters. The CLI cannot detect this for you.7. **`state` → action → `state` after a page change.** Navigations, form submits, and SPA route changes invalidate refs. Take a fresh snapshot. Do not reuse refs from before the transition.8. **Chain with `&&` when reusing freshly parsed refs.** A chained sequence runs in one shell so the ref you just read from output can be passed directly to the next command. Separate shell invocations keep the named browser session, but any shell-local variables or copied refs from the previous command can go stale after page changes.

The network feature can read full response bodies and caches captured results in the user's home directory with a default 24-hour TTL.

View source
SKILL.md:199In the instructionsOpen original file
```bashbrowser network                        # shape preview + cache key listbrowser network --detail <key>         # full body for one cached entrybrowser network --filter "field1,field2"  # keep only entries whose body shape contains ALL fields as path segmentsbrowser network --all                  # include static resources (usually noise)browser network --raw                  # full bodies inline — large; use sparinglybrowser network --ttl <ms>             # cache TTL (default 24h)```List entries look like `{key, method, status, url, ct, size, shape, body_truncated?}`. Detail envelope is `{key, url, method, status, ct, size, shape, body, body_truncated?, body_full_size?, body_truncation_reason}`. Cache lives in `~/.opencli/cache/browser-network/` so you can re-inspect without re-triggering the request.

After binding an existing tab, the Skill permits navigation but blocks tab creation, selection, and closure; the binding has no idle-close timer.

View source
SKILL.md:43In the instructionsOpen original file
Binding never owns the user window and never closes the user tab. It fails closed if the tab is closed or becomes non-debuggable. Re-run `opencli browser <session> bind` when you switch to a different real tab.Navigation is allowed on bound sessions because the session now represents explicit agent ownership of that tab. Tab mutation (`tab new`, `tab select`, `tab close`) is still blocked for bound sessions. Use an owned session when you want OpenCLI to manage tab lifecycle.Bound sessions have no OpenCLI idle-close timer; the binding lasts until `unbind`, tab close, window close, or daemon restart.
Start here · InstructionsSKILL.md
opencli-browser
Lines connect the instruction file to its sections, not an observed execution order. Select a section to read the source. 6 more sections are available in the original file.
Files and check records1 files

Coverage and gaps

Content covered in each file

These are the source ranges included in this check, not a guarantee that every issue has been resolved.

  • SKILL.mdFull text included

This report is for the version above. We read the available code and instructions without running the skill or checking extra packages it installs. This is not a promise of safety: a different version or setup may behave differently.

  • SKILL.mdInstructions

Operations mentioned in code and instructions

Run commands
SKILL.md:4In the instructionsOpen original file
description: Use when an agent needs to drive a real Chrome window via opencli — inspect a page, fill forms, click through logged-in flows, or extract data ad-hoc. Covers the selector-first target contract, compound form fields, stale-ref h allowed-tools: Bash(opencli:*), Read, Edit, Write---
SKILL.md:17In the instructionsOpen original file
```bashopencli doctor
SKILL.md:35In the instructionsOpen original file
```bashopencli browser gmail bind
Connect to websites
SKILL.md:28In the instructionsOpen original file
- `opencli browser *` commands require a `<session>` positional immediately after `browser`. Use the same session name for a multi-step flow; use a different name to isolate parallel browser work.- Use a stable session name for any multi-command or human-paced browser workflow. Example: `opencli browser fb-yaya-warmup open https://example.com`, then reuse `opencli browser fb-yaya-warmup state`, `extract`, `click`, etc.- Owned browser sessions keep a tab lease alive between calls. Release it with `opencli browser <session> close` or let the idle timeout expire.
SKILL.md:310In the instructionsOpen original file
```bashopencli browser hn open "https://news.ycombinator.com" \  && opencli browser hn state \
SKILL.md:326In the instructionsOpen original file
```bashopencli browser login open "https://example.com/login"opencli browser login state                          # find [N] for email, password, submit
Lines read
445
File checksum (to compare versions)
ac0de7d5b6f6c60517671fdad24f5d132d646fa54ca284a9fddb41820f6793e1