Skyvern automates work in a web browser by looking at the page the way a person does. Instead of a script full of CSS selectors that breaks when a site changes its layout, it gives a vision-capable language model a screenshot and the page structure, and lets it decide what to click, type and select to reach a goal you describe, such as filling in a form, downloading invoices from a portal or getting a quote.
There are three ways to use it. As a Python or TypeScript SDK it extends Playwright, so page.click can take a prompt like Click the green Submit button, and page.extract returns structured data matching a schema. As a local server it comes with a web UI and a no-code workflow builder that chains tasks, loops, data extraction and file downloads, with logins, two-factor codes and credentials handled through built-in integrations. And there is a managed cloud version from the same team.
It is AGPL-3.0, with about 23,200 stars, at v1.0.56 when scanned. The anti-bot measures used in the cloud version are not part of the open source code.
- Repository: github.com/Skyvern-AI/skyvern
- Licence: AGPL-3.0 (GNU Affero General Public License v3.0)
- Language: Python. Stars: 23.2K. Forks: 2,203. Last push: Oct 22, 2026.
- Scan: safe, Oct 22, 2026, commit 73e49ce
Who it is for
Operations teams with repetitive browser work across sites that have no API, developers who already use Playwright and want AI fallbacks, and builders of agents that need to act on real websites.
Getting started
1. Install the server and web UI (Python 3.11 to 3.13)
pip install "skyvern[all]"2. Run the guided setup: picks your LLM provider and starts everything
skyvern quickstart3. Open the web UI
open http://localhost:8080The local setup stores its data in SQLite at ~/.skyvern/data.db by default; Docker Compose and Postgres are options. Every step calls a vision model, so long workflows add up on a paid API. Set SKYVERN_TELEMETRY=false before starting to turn off usage statistics.
Safety scan
We cloned Skyvern-AI/skyvern at commit 73e49ce on Oct 22, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- No secrets and no committed binaries across 5,595 files and about 1.8 million lines of Python and TypeScript.
- The pattern hits are explained: install.sh documents the project's own installer at install.skyvern.com and itself downloads before executing rather than piping into a shell, two Dockerfiles install uv, and the two exfiltration-host hits are error messages telling users not to use webhook.site or requestbin.com as webhook targets. Five very long lines are a bundled browser extension script and test fixtures.
- The README states that basic usage statistics are collected by default, with SKYVERN_TELEMETRY=false to opt out.
- 55 known advisories (3 critical), 38 of them in uv.lock. The criticals are Pillow image-decoding bugs; Skyvern mostly decodes its own browser screenshots. The rest are in the frontend and TypeScript SDK build tooling.
- 13 workflows. One uses pull_request_target to sync to the cloud repository and does not check out the pull request; all 15 third-party actions are pinned to commits. Security policy, licence and contributing guide present; no Dependabot or CodeQL.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | None found. |
| Suspicious code | 15 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 1 npm lifecycle script. 5 installer scripts (one fetches and runs a remote script) |
| Committed binaries | None. |
| CI workflows | 13 workflows. 1 uses pull_request_target, none check out the pull request head. 0 of 15 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often example.test, api.skyvern.com, portal.example.com, www.googleapis.com. 15 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 55 advisories across 1,461 pinned packages: 3 critical, 24 high, 25 moderate, 3 low. skyvern-frontend/package-lock.json: 845 packages, 15 advisories; skyvern-ts/client/package-lock.json: 221 packages, 7 advisories; skyvern-ts/client/pnpm-lock.yaml: 235 packages, 8 advisories; skyvern/cli/mcpb/claude_desktop/package-lock.json: 81 packages, 0 advisories; tests/sdk/typescript_sdk/package-lock.json: 83 packages, 4 advisories; uv.lock: 404 packages, 38 advisories. |
| Project hygiene | Has security policy, licence file, contributing guide. Missing automated dependency updates, CodeQL. |
| OpenSSF Scorecard | Not scored: the project is not in Scorecard's weekly index. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Pattern hits (15)
| Where | Rule | Match |
|---|---|---|
| Dockerfile:5 | download-piped-to-shell | RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ |
| install.sh:25 | download-piped-to-shell | curl -LsSf https://install.skyvern.com | sh |
| install.sh:26 | download-piped-to-shell | curl -LsSf https://install.skyvern.com | sh -s -- --no-init |
| install.sh:27 | download-piped-to-shell | curl -LsSf https://install.skyvern.com | sh -s -- --run-setup |
| install.sh:28 | download-piped-to-shell | curl -LsSf https://install.skyvern.com | sh -s -- --version 1.0.35 |
| install.sh:29 | download-piped-to-shell | curl -LsSf https://install.skyvern.com | sh -s -- --variant server |
| install.sh:75 | download-piped-to-shell | # Download then execute (not `curl ... | sh`): POSIX /bin/sh has no |
| integrations/mcp/Dockerfile:4 | download-piped-to-shell | RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ |
| skyvern/browser_extension/extension/control_indicator.js:129 | very-long-line | 6007 chars |
| skyvern/browser_extension/relay.py:328 | very-long-line | 6027 chars |
| skyvern/browser_extension/relay.py:345 | very-long-line | 6033 chars |
| skyvern/forge/sdk/api/llm/utils_test.py:316 | very-long-line | 7176 chars |
| skyvern/forge/sdk/api/llm/utils_test.py:437 | very-long-line | 16812 chars |
| skyvern/forge/sdk/routes/webhooks.py:84 | exfil-host | "such as webhook.site or requestbin.com." |
| skyvern/services/webhook_service.py:606 | exfil-host | "such as webhook.site or requestbin.com." |
URLs to bare IP addresses (15)
| Where | Rule | Match |
|---|---|---|
| skyvern/webeye/schemas.py:59 | ip-literal-url | examples=["http://localhost:9222", "https://3.12.10.11/browser/123456"], |
| tests/unit/test_real_browser_state_navigate.py:193 | ip-literal-url | page.url = "https://93.184.216.34/current" |
| tests/unit/test_remote_cdp_driver_release.py:351 | ip-literal-url | await RealBrowserManager()._create_browser_state(browser_address="http://192.0.2.10:9222") |
| tests/unit/test_remote_cdp_driver_release.py:431 | ip-literal-url | remote_state = await RealBrowserManager()._create_browser_state(browser_address="http://192.0.2.10:9222") |
| tests/unit/test_script_skyvern_page.py:156 | ip-literal-url | await page.goto("https://93.184.216.34/path") |
| tests/unit/test_script_skyvern_page.py:162 | ip-literal-url | url="https://93.184.216.34/path", |
| tests/unit/test_script_skyvern_page.py:173 | ip-literal-url | await page.goto("https://93.184.216.34/path", max_retries=1) |
| tests/unit/test_script_skyvern_page.py:208 | ip-literal-url | url = "https://93.184.216.34/path" |
| tests/unit/test_script_skyvern_page.py:265 | ip-literal-url | rendered_url = "https://93.184.216.34/path" |
| tests/unit/test_task_request_validation.py:341 | ip-literal-url | TaskRunRequest(prompt="t", browser_address="http://1.2.3.4:9222", start_fresh_browser=True) |
| tests/unit/test_task_request_validation.py:347 | ip-literal-url | TaskRunRequest(prompt="t", browser_address="http://1.2.3.4:9222") |
| tests/unit/test_task_request_validation.py:357 | ip-literal-url | WorkflowRunRequest(agent_id="wpid_1", browser_address="http://1.2.3.4:9222", start_fresh_browser=True) |
| tests/unit/test_task_request_validation.py:363 | ip-literal-url | WorkflowRunRequest(agent_id="wpid_1", browser_address="http://1.2.3.4:9222") |
| tests/unit/test_task_request_validation.py:373 | ip-literal-url | WorkflowRequestBody(browser_address="http://1.2.3.4:9222", start_fresh_browser=True) |
| tests/unit/test_task_request_validation.py:405 | ip-literal-url | browser_address="http://1.2.3.4:9222", |
npm lifecycle scripts (1)
skyvern-frontend/package.jsonprepare:cd .. && command -v pre-commit >/dev/null 2>&1 && pre-commit install || echo 'pre-commit not installed, skipping hook setup'
Installer scripts (5)
- install.sh, 169 lines, fetches and runs a remote script; talks to astral.sh, docs.astral.sh, install.skyvern.com
- run_alembic_check.sh, 33 lines
- run_skyvern.sh, 16 lines
- run_ui.sh, 17 lines
- scripts/start_vnc_streaming.sh, 78 lines
Worst known vulnerabilities (24 of 55)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-332p-7hrc-92wf | critical | pillow@12.3.0 | Pillow: Heap buffer overflow in _tiffWriteProc when saving TIFF to BytesIO |
| GHSA-8g5g-8vmr-9782 | critical | pillow@12.3.0 | Pillow: Heap buffer overflow in ImagingJpegDecode |
| GHSA-w889-685w-c8fh | critical | pillow@12.3.0 | Pillow: Argument Injection in Ghostscript call, potentially leading into RCE |
| GHSA-vfj7-8cjw-p6xm | high | braces@3.0.3 | braces vulnerable to stack-exhaustion denial of service through deeply nested patterns |
| GHSA-8r9p-f939-6h3c | high | follow-redirects@1.16.0 | follow-redirects sensitiveHeaders regex escaping can expose credential headers |
| GHSA-q3jh-wrc6-jgq8 | high | follow-redirects@1.16.0 | follow-redirects array-form headers bypass cross-origin credential stripping |
| GHSA-587h-7wmj-h5pm | high | vite@6.4.3 | launch-editor: `fs.existsSync()` existence check can be bypassed on Windows |
| GHSA-587h-7wmj-h5pm | high | vite@7.3.6 | launch-editor: `fs.existsSync()` existence check can be bypassed on Windows |
| GHSA-22hg-r758-rrw9 | high | pillow@12.3.0 | Pillow: Infinite loop in MIC n_frames when the embedded TIFF |
| GHSA-27h8-gf7j-v5hc | high | pillow@12.3.0 | Pillow: NULL dereference in ImagingExpand via rank filters on zero-size images |
| GHSA-58r4-6rv3-gfh8 | high | pillow@12.3.0 | Pillow: Out-of-bounds access in _imagingmorph on a zero-width image |
| GHSA-5xm2-6676-qcp9 | high | pillow@12.3.0 | Pillow: APNG background disposal allocates full canvas before decompression bomb check |
| GHSA-65qm-jvmh-6pqg | high | pillow@12.3.0 | Pillow: GIF seek keeps rejected size after DecompressionBombError, allowing DoS |
| GHSA-7mwm-ghpf-9vrm | high | pillow@12.3.0 | Pillow: Double free in ImagingScaleAffine on calloc failure via Image.transform |
| GHSA-c6hw-5grf-ff96 | high | pillow@12.3.0 | Pillow: OOB read in TiffDecode.c tile and RGBA paths leaks heap memory into pixels |
| GHSA-cp9v-662q-vvx9 | high | pillow@12.3.0 | Pillow: NULL pointer dereference in Image.merge via an unvalidated band 0 |
| GHSA-f2g6-542v-9j58 | high | pillow@12.3.0 | Pillow: Infinite loop in PdfParser.rewrite_pages on page tree Parent cycle |
| GHSA-gg2r-wvfm-q9rx | high | pillow@12.3.0 | Pillow: MSP RLE decoder expands a tiny file into unbounded memory |
| GHSA-j4m9-6w36-pw47 | high | pillow@12.3.0 | Pillow: Invalid free in FreeType glyph rendering when a stroker is active |
| GHSA-jg86-v3jv-hrpm | high | pillow@12.3.0 | Pillow: Quadratic-time DoS in PDF save_all (PdfParser.next_object_id) |
| GHSA-p4wg-p73j-c528 | high | pillow@12.3.0 | Pillow: Unbounded recursion parsing a PDF page tree or trailer chain (incomplete fix for CVE-2026-42310) |
| GHSA-pchr-8j38-3x4p | high | pillow@12.3.0 | Pillow: TIFF encoder ColorMap plane-size mismatch causes a heap out-of-bounds read |
| GHSA-q45j-58j2-45rg | high | pillow@12.3.0 | Pillow: TiffDecode returns uninitialized memory as pixels for short JPEG strips |
| GHSA-xvh5-g6gv-559v | high | pillow@12.3.0 | Pillow: Memory disclosure and segfault in libtiff encoder via crafted TIFF tags |
Workflows worth a look
- .github/workflows/sync-skyvern-cloud.yml: pull_request_target
By the numbers
| Stars | 23.2K |
|---|---|
| Forks | 2,203 |
| Contributors | 96 |
| Commits | 7,076 |
| Open issues | 50 |
| Open pull requests | 240 |
| Releases | 160 |
| Latest release | v1.0.56 |
| Licence | AGPL-3.0 |
| Main language | Python |
| Project age | 2 years |
| Last push | Oct 22, 2026 |
| Tracked files | 5,595 |
| Lines of code | 1.8M |
| Checkout size | 262 MB |
Lines by language: Python 1.3M, TypeScript 417.1K, JSON 95.4K, Markdown 32.6K, JavaScript 18.9K, YAML 5,013.
Questions
Is Skyvern free?
The open source version is free to self-host under the AGPL-3.0 licence; you pay for the language model calls it makes, or nothing with a local vision model. Skyvern Cloud is a paid managed service with extras such as anti-bot handling and parallel runs.
How is Skyvern different from browser-use?
Both let an AI drive a browser. browser-use is a Python library for agents. Skyvern adds a server with a web UI and a no-code workflow builder, credential and two-factor handling, and a Playwright-compatible SDK where AI is a fallback for ordinary selectors, so it leans more towards repeatable business workflows.
Which models does Skyvern work with?
Any provider with a vision-capable model that its setup supports, including OpenAI, Anthropic, Google Gemini, Azure, Bedrock and OpenRouter, plus local models through Ollama or an OpenAI-compatible server. Larger vision models are noticeably more reliable on complex sites.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
