Back to skill

Security audit

Browser Steel

Security checks for vulnerabilities and agentic risk

Overview

This browser automation skill is coherent and not clearly malicious, but it deserves review because it can run an unpinned downloaded CLI and implicitly use local environment, cookie, profile, and session material.

Install only if you trust the Steel CLI source and are comfortable with broad browser automation. Prefer a preinstalled, pinned Steel CLI over the `npx` fallback, run it with a minimal environment, avoid placing unrelated secrets in workspace `.env` files, and provide cookie/profile/session inputs only for accounts and sites you are authorized to automate.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T08 · Insecure Dependencies

Warning
Location
scripts/main.py:106
Finding

Unpinned npm Dependency Is Downloaded and Executed Automatically

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py, lines 106-118
Vulnerability Type: Unpinned runtime dependency and mutable supply-chain execution
Risk Level: Medium

Vulnerable Code

python
if runtime == "node":
    if _command_exists("npx"):
        return ["npx", "--yes", "@steel-dev/cli"], "npx"
    raise SkillError("runtime=node requires `npx` (or set STEEL_BROWSER_CLI_BIN)")

if runtime in ("auto", "cli"):
    if _command_exists("steel"):
        return ["steel"], "steel"
    if _command_exists("npx"):
        return ["npx", "--yes", "@steel-dev/cli"], "npx"
    raise SkillError("Steel CLI not found. Install `steel` or make `npx` available.")

Technical Analysis

When a locally installed steel executable is unavailable, the default auto runtime falls back to:

text
npx --yes @steel-dev/cli

The package has no exact version or verified integrity value. Consequently, npm may download and immediately execute whichever package version the registry resolves at runtime. The --yes option removes the interactive installation confirmation.

This creates a mutable execution path: the code ultimately executed can change after this skill has been reviewed. Although the referenced package name appears consistent with the intended Steel integration, the implementation does not constrain execution to an audited release.

The child process inherits the merged environment passed by the wrapper. That environment may contain STEEL_API_KEY and other process credentials or configuration values. Therefore, a malicious or compromised dependency release could access those values with the privileges of the user running the skill.

Attack Path

  1. An attacker compromises the relevant npm package, its publisher account, or the package-distribution path and publishes a malicious release.
  2. The target environment does not have a local steel executable, but it does have `npx ...[truncated 1325 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin @steel-dev/cli to a reviewed exact version rather than resolving the latest available release:

    python
    return ["npx", "--yes", "@steel-dev/cli@X.Y.Z"], "npx"
    
  2. Prefer installing dependencies during a controlled setup phase instead of downloading executable code during normal skill operation.

  3. Maintain a lockfile with integrity metadata and use a reproducible installation command such as npm ci.

  4. Verify package provenance, signatures, and registry configuration where supported.

  5. Remove the silent automatic fallback or require explicit user approval before downloading a missing executable dependency.

  6. Execute the CLI with a minimal allowlisted environment rather than forwarding the entire process environment. Include only variables required by Steel.

  7. Run browser automation in a sandbox or restricted account with limited filesystem and network access.

  8. Establish a dependency update process in which new versions are reviewed and tested before changing the pinned release.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (22)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 36)May include surrounding context.

md
- **`scripts/main.py`** = the wrapper that calls Steel CLI by default

Chaining Abuse

High
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Content

Scanner excerpt · scripts/main.py (reported line 7)May include surrounding context.

python
CLI-first browser automation for Steel.
- Default runtime: Steel CLI (`steel` or `npx @steel-dev/cli`)
- Optional runtime: Python Playwright + Steel SDK plan runner
- Runtime choice: auto | cli | node | python
"""

from __future__ import print_function

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The skill is designed to look for a .env file in the current working directory, which often contains secrets such as API keys or session credentials. In an agent environment, automatic credential discovery from user workspaces materially increases the chance of unintended secret access and misuse.

Content

Scanner excerpt · scripts/main.py (reported line 25)May include surrounding context.

python
SKILL_DIR = Path(__file__).resolve().parents[1]
DEFAULT_ENV_CANDIDATES = [
    Path.cwd() / ".env",
    SKILL_DIR / ".env",
]
SUPPORTED_PYTHON_ACTIONS = [

Credential Access

High
Category
Privilege Escalation
Confidence
87% confidence
Finding

The skill also auto-loads a .env file from the skill directory, creating another implicit credential source. While somewhat more controlled than the working directory case, it still enables hidden secret consumption and behavior changes without clear user intent.

Content

Scanner excerpt · scripts/main.py (reported line 26)May include surrounding context.

python
SKILL_DIR = Path(__file__).resolve().parents[1]
DEFAULT_ENV_CANDIDATES = [
    Path.cwd() / ".env",
    SKILL_DIR / ".env",
]
SUPPORTED_PYTHON_ACTIONS = [
    "goto",

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
93% confidence
Finding

Copying the full process environment into the skill gives it access to all ambient secrets, tokens, and configuration present in the parent context. In this skill, those values may then influence subprocess execution or remote browser connectivity, making broad environment harvesting more dangerous than in a simple standalone script.

Content

Scanner excerpt · scripts/main.py (reported line 77)May include surrounding context.

python
def _load_env(env_file=None):
    merged = dict(os.environ)
    chosen = None

    candidates = []

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill invokes shell commands, reads environment variables, accesses files, and performs network operations, but it does not declare any explicit tool scope such as allowed-tools or permissions. That omission weakens least-privilege boundaries and can let an agent use broader capabilities than reviewers or platform policy expect, which is especially risky for a browser automation skill that can reach arbitrary URLs and handle session material.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

Using npx @steel-dev/cli without a pinned version introduces a supply-chain risk because execution may fetch the latest package at runtime. If the upstream package is compromised or a breaking release is published, the skill could execute unreviewed code in the agent environment.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

This runtime mode again specifies npx @steel-dev/cli without a pinned version, creating the same supply-chain exposure during tool resolution. In a skill that defaults to external browser automation and shell execution, unpinned remote package execution materially increases attack surface.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The documentation explicitly enables proxy-backed and CAPTCHA-solving browser sessions without any guardrails, warnings, or policy constraints. In a browser automation skill, these capabilities can facilitate evasion of anti-bot controls and obscure request origin, increasing the risk of misuse against third-party sites or in ways that violate privacy, terms of service, or compliance obligations.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

The documentation instructs the runtime to execute npx --yes @steel-dev/cli without pinning an exact version, which allows whatever package version is current at execution time to be fetched and run. In a browser automation skill, that creates a real supply-chain risk because the fetched CLI may gain new behavior, introduce malicious code, or change security properties while still being executed with the user's local permissions and access to browser/session data.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

This CLI-only mode explicitly falls back to npx --yes @steel-dev/cli with no version pin, so the skill may download and execute unreviewed code at runtime. Because the skill operates on live websites, sessions, cookies, and potentially credentials, an attacker controlling or compromising the package supply chain could exfiltrate sensitive data or execute arbitrary actions in the user's environment.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding

The node mode forces use of npx --yes @steel-dev/cli even when a local installed steel binary exists, increasing exposure to remote package resolution and execution. For a browser automation skill, forcing the network-fetched path makes the supply-chain risk more dangerous because the tool can access authenticated browser sessions, page contents, screenshots, and automation flows.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

Using 'npx --yes @steel-dev/cli' without a pinned version allows the fetched package version to vary over time. That creates a supply-chain risk where a compromised or unexpected upstream release can alter what code this skill executes.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/main.py (reported line 124)May include surrounding context.

python
def _run_subprocess(command, env):
    completed = subprocess.run(
        command,
        env=env,
        stdout=subprocess.PIPE,

Tainted flow: 'command' from os.environ.get (line 448, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
92% confidence
Finding

Although subprocess.run itself is used safely with a list, the command can be tainted by environment-controlled executable selection such as STEEL_BROWSER_CLI_BIN. Combined with automatic .env loading, a malicious local configuration can redirect execution to an arbitrary binary.

Content

Scanner excerpt · scripts/main.py (reported line 124)May include surrounding context.

python
def _run_subprocess(command, env):
    completed = subprocess.run(
        command,
        env=env,
        stdout=subprocess.PIPE,

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/main.py (reported line 256)May include surrounding context.

python
for module_name in ("playwright", "steel"):
        try:
            __import__(module_name)
            report["current_modules"][module_name] = True
        except Exception:
            report["current_modules"][module_name] = False

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The re-exec path invokes a Python interpreter taken from STEEL_BROWSER_PYTHON_BIN, which can come from the environment or loaded .env file. That gives an attacker who can influence configuration a direct arbitrary-code-execution primitive by pointing to a malicious executable, and this skill explicitly auto-loads local .env files, increasing exposure.

Content

Scanner excerpt · scripts/main.py (reported line 449)May include surrounding context.

python
return None

    command = [str(target)] + sys.argv
    return subprocess.call(command, env=env)


async def _run_python_plan_async(args, env):

Tainted flow: 'command' from os.environ.get (line 448, credential/environment) → subprocess.call (code execution)

Medium
Category
Data Flow
Confidence
97% confidence
Finding

The command is built from STEEL_BROWSER_PYTHON_BIN and then executed, creating a straightforward tainted flow from environment/configuration to code execution. Because the skill auto-loads .env files from the working directory and skill directory, this becomes a practical local execution risk.

Content

Scanner excerpt · scripts/main.py (reported line 449)May include surrounding context.

python
return None

    command = [str(target)] + sys.argv
    return subprocess.call(command, env=env)


async def _run_python_plan_async(args, env):

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The API key is interpolated directly into a WebSocket connection URL for a remote service. While this may be required by the SDK protocol, embedding secrets in URLs increases accidental leakage risk through logs, error messages, telemetry, or debugging output.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Cookie data is loaded from a local file into the browser context and then used against potentially remote sites, effectively importing authentication material into automated browsing. In an agent skill, that can silently reuse sensitive sessions and enable account access or data extraction without clear user awareness.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The generic browser passthrough exposes arbitrary Steel CLI browser subcommands rather than a narrow, declared set of workflows. In an agent setting, that broadens capability beyond expected boundaries and can enable unsafe actions or data access paths not reviewed in the skill manifest.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill automatically searches for and loads arbitrary local .env files, merging their contents into the execution environment. That expands scope from browser automation into local secret ingestion and configuration-based behavior control, which is risky in an agent context.

Content

No source excerpt is available for this finding.