Back to skill

Security audit

yumweb

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real logged-in browser automation bridge, but it gives agents broad authenticated browser control and public posting ability without enough guardrails.

Install only if you are comfortable giving an agent control over a dedicated logged-in browser profile. Use a separate low-risk profile and accounts, avoid sensitive inboxes or commerce sessions unless needed, manually approve posts/purchases/form submissions/eval, add a .gitignore or move the profile outside the repo, pin dependencies, and restrict CDP to loopback without wildcard origins before serious use.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/yumweb.py:347
Finding
Authenticated Browser Exposed Through Unrestricted CDP Origins<![CDATA[ ## Vulnerability Details **File Location**: `scripts/yumweb.py:347-355` **Vulnerability Type**: Unrestricted Chrome DevTools Protocol access **Risk Level**: High ### Vulnerable Code ```python args = [ edge, f"--remote-debugging-port={port}", f"--user-data-dir={profile}", "--no-first-run", "--no-default-browser-check", "--disable-features=msEdgeFirstRunExperience", "--remote-allow-origins=*", "about:blank", ] ``` ### Technical Analysis The browser is started with Chrome DevTools Protocol enabled and with `--remote-allow-origins=*`. This wildcard allows WebSocket upgrade requests from arbitrary origins instead of limiting CDP clients to trusted origins. CDP grants extensive control over the browser, including reading page content, executing JavaScript, navigating tabs, submitting forms, and interacting with authenticated sessions. This is particularly sensitive because yumweb deliberately uses a persistent browser profile containing login cookies. The launch arguments do not explicitly include `--remote-debugging-address=127.0.0.1`. Although current Chromium implementations normally bind remote debugging to loopback by default, relying on an implicit default is weaker than enforcing the intended network boundary explicitly. ### Attack Path 1. The user starts yumweb and logs into sensitive websites using its persistent browser profile. 2. Chromium exposes CDP on port 9333 with arbitrary WebSocket origins allowed. 3. A hostile local process, or a malicious origin capable of discovering and reaching the local endpoint, attempts to connect to the CDP service. 4. The wildcard origin setting permits the WebSocket origin. 5. The attacker uses CDP methods to inspect authenticated pages, execute JavaScript, navigate tabs, submit actions, or extract sensitive page data. Exploitation depends on the attacker being able to reach or discover the local CDP endpoint. If the endpoint becomes accessible beyond loopback because of ...[truncated 784 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the unrestricted origin option: ```python "--remote-allow-origins=*", ``` 2. Explicitly enforce loopback binding: ```python f"--remote-debugging-address=127.0.0.1", f"--remote-debugging-port={port}", ``` 3. Use an unpredictable, dynamically allocated port where practical instead of a fixed port. 4. Verify after startup that the CDP socket is bound exclusively to loopback; terminate the browser if this verification fails. 5. Isolate the browser process from untrusted local users and containers. 6. Consider an authenticated local proxy or OS-protected IPC mechanism rather than exposing CDP directly. 7. Warn users that any process capable of connecting to CDP can effectively control authenticated browser sessions. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/bootstrap.sh:10
Finding
Automatic Installation of Unpinned Third-Party Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bootstrap.sh:10-15`, `requirements.txt:1-4`, `scripts/yumweb.py:516-521` **Vulnerability Type**: Uncontrolled dependency resolution and runtime package installation **Risk Level**: Medium ### Vulnerable Code `scripts/bootstrap.sh:10-15`: ```bash if [ ! -d "$VENV_DIR" ]; then "$PYTHON_BIN" -m venv "$VENV_DIR" fi "$VENV_DIR/bin/python" -m pip install -r "$SKILL_DIR/requirements.txt" >/dev/null ``` `requirements.txt:1-4`: ```text playwright>=1.40 html2text>=2024.2.26 requests>=2.31 psutil>=5.9 ``` `scripts/yumweb.py:516-521`: ```python def _ensure_html2text(): try: import html2text # noqa return html2text except ImportError: subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "html2text"]) import html2text # noqa return html2text ``` ### Technical Analysis Every invocation through `scripts/run.sh` executes the bootstrap script, which invokes pip against dependencies specified only with open-ended minimum versions. Consequently, dependency resolution can select future releases that did not exist when the project was audited. The dependency manifest contains no exact version pins or package hashes. In addition, `_ensure_html2text()` performs an implicit package installation during normal runtime if the import is unavailable. This installation does not specify even a minimum version and is not clearly separated into an explicit setup step. Packages installed by pip can execute code during build or installation, and imported packages execute module initialization code with the privileges of the current user. Therefore, compromise of a dependency release or its distribution channel can turn a routine skill invocation into local code execution. No evidence was found that the currently named dependencies are intentionally malicious. The vulnerability is the unsafe, mutable supply-chain configuration. ### Attack Path 1. An att ...[truncated 1274 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every dependency to an exact, reviewed version: ```text playwright==<reviewed-version> html2text==<reviewed-version> requests==<reviewed-version> psutil==<reviewed-version> ``` 2. Generate and verify cryptographic hashes for all distributions, and install with: ```bash python -m pip install --require-hashes -r requirements.txt ``` 3. Maintain a lock file generated from a trusted environment and review dependency updates before merging them. 4. Move installation into an explicit setup command rather than executing pip on every normal skill invocation. 5. Remove runtime installation from `_ensure_html2text()`. If the package is absent, return a clear error directing the user to the explicit setup procedure. 6. Prefer binary wheels from trusted indexes and explicitly configure the approved package index. 7. Add automated dependency vulnerability and provenance scanning to the release process. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/yumweb.py:340
Finding
Persistent Credential-Bearing Browser Profile Is Not Excluded from Version Control<![CDATA[ ## Vulnerability Details **File Location**: `scripts/yumweb.py:340-344`; related documentation at `SKILL.md:140-142` **Vulnerability Type**: Insecure storage and accidental disclosure of browser credentials **Risk Level**: Medium ### Vulnerable Code `scripts/yumweb.py:340-344`: ```python edge = find_edge_exe(cfg) profile = cfg["profile_dir"] Path(profile).mkdir(parents=True, exist_ok=True) log_path = cfg["log_path"] Path(log_path).parent.mkdir(parents=True, exist_ok=True) ``` The default configuration resolves the profile directory inside the project. The documentation states: ```markdown - Cookies live in `profile/` — treat that directory like a credential. The bundled `.gitignore` excludes it. Don't commit it. ``` However, the audited project directory contains no `.gitignore` file. Therefore, the documented safeguard is absent. ### Technical Analysis The skill intentionally persists browser state under the project’s default `profile/` directory. Such Chromium profiles may contain cookies, session databases, browsing history, autofill data, local storage, service-worker data, and other sensitive account state. Storing the profile inside the repository tree increases the chance that it will be included in a source-control commit, archive, backup, or skill package. The documentation claims that a bundled `.gitignore` excludes the directory, but no such file exists in the audited artifact. A `.gitignore` is not a complete security boundary, but its absence removes the specific accidental-disclosure protection promised to users. ### Attack Path 1. The user starts yumweb, generating `profile/` beneath the project directory. 2. The user logs into email, social-media, shopping, or other sensitive services. 3. Chromium stores persistent authenticated state and related browser data in `profile/`. 4. The user runs a broad source-control add operation, packages the project directory, shares an archive, or uploads the skill workspace. 5. Because ...[truncated 1022 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add and ship a project-root `.gitignore` containing at least: ```gitignore profile/ logs/ .venv/ *.pid *.png ``` 2. Store the profile outside the repository by default, using the operating system’s private application-data directory. 3. Create the profile and log directories with permissions restricted to the current user. 4. Add startup checks that warn or refuse to continue if the profile is inside a Git repository and is not ignored. 5. Add a pre-commit rule that rejects Chromium profile databases, cookie files, logs, and browser artifacts. 6. Correct the documentation so it does not claim that a safeguard is bundled unless it is present in released artifacts. 7. Document secure profile deletion, backup, and incident-response procedures for users who may have accidentally published the directory. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description understates the true power of the skill: it can launch/manage a separate persistent browser profile, execute arbitrary JavaScript in live pages, capture screenshots, and perform stronger authenticated actions such as posting to X. This mismatch is dangerous because users or orchestrating agents may treat it as a read/check tool when it can actually modify accounts, exfiltrate page data, and run arbitrary in-page code in logged-in sessions.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The eval command allows arbitrary JavaScript execution in the context of whatever logged-in page is active. In a browser automation skill intended to act as an agent's hands, this greatly expands power: it can read page state, extract sensitive data, trigger privileged actions, and manipulate authenticated sessions beyond ordinary click/type automation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly promotes agent access to highly sensitive logged-in contexts such as Gmail, Outlook, social feeds, and Amazon carts, but the surrounding guidance does not provide a prominent, explicit warning that an agent may view private data or perform unintended actions under the user's authenticated identity. In this skill's context, that omission is especially dangerous because the whole purpose of the tool is to let an agent act inside persistent authenticated sessions, increasing the risk of privacy breaches, account misuse, and accidental transactions or posts.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The release notes explicitly promote using a persistent, already-logged-in browser as an AI agent's operating surface, but they do not include a clear warning about the privacy, account-access, and data-exposure implications of that model. In this skill context, that omission is more dangerous than usual because the core product value is access to real authenticated sessions across email, social, shopping, and messaging sites, which materially increases the risk of unintended sensitive actions or data access.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill exposes powerful capabilities including shell execution, network access, file read/write behavior, and control of a persistent logged-in browser profile, but it declares no tool scope or permission boundaries. In an agent setting, that omission increases the chance the skill will be invoked with excessive authority, enabling actions on authenticated sites and access to sensitive local/browser data without explicit consent controls.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The invocation guidance is broad enough to trigger the skill for generic web-browsing requests, encouraging automatic use whenever a user asks to check websites or social platforms. In the context of a logged-in persistent browser, over-broad routing can cause an agent to access private sessions, sensitive inboxes, or authenticated content without a clear user acknowledgment that logged-in state will be used.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill provides posting and interactive capabilities on logged-in websites, but the usage guidance does not require a user warning or confirmation before actions that can change account state or publish content. Because this skill operates inside authenticated browser sessions, missing warnings can lead to unintended messages, posts, clicks, purchases, or disclosure of sensitive account data.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The metadata says the skill reuses an already logged-in browser, but the code actually launches a separate dedicated profile. This mismatch can mislead operators about what accounts, cookies, and browsing context are being used, increasing the chance of unsafe assumptions and accidental credential handling mistakes.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_version(port: int) -> Optional[dict]:
    try:
        with urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=2) as r:
            return json.loads(r.read())
    except Exception:
        return None
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_version(port: int) -> Optional[dict]:
    try:
        with urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=2) as r:
            return json.loads(r.read())
    except Exception:
        return None
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
def get_cdp_version(port: int) -> Optional[dict]:
    try:
        with urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=2) as r:
            return json.loads(r.read())
    except Exception:
        return None
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
with open(log_path, "ab") as logf:
        popen_kwargs["stdout"] = logf
        popen_kwargs["stderr"] = logf
        proc = subprocess.Popen(args, **popen_kwargs)

    # Record the launched PID so `stop` can find it without scanning processes.
    try:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The stop workflow scans for and kills local processes based on profile matching, which exceeds the advertised browser interaction role. This can affect unrelated local processes if matching is imprecise, and in an agent setting it grants unnecessary control over the host operating environment.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def _kill_pid(pid: int) -> bool:
    try:
        if IS_WINDOWS:
            subprocess.run(
                ["taskkill", "/F", "/T", "/PID", str(pid)],
                stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
                check=False, timeout=10,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
f"Where-Object {{ $_.CommandLine -like '*{profile.replace(chr(92), chr(92)+chr(92))}*' }} | "
                "ForEach-Object { $_.ProcessId }"
            )
            out = subprocess.check_output(
                ["powershell", "-NoProfile", "-Command", ps_cmd],
                text=True, timeout=15,
            )
Confidence
82% confidence
Finding
The PowerShell command string is built by embedding the profile path into a -Command script. If an attacker can influence the profile path in config.json, special characters may alter the PowerShell expression and cause unintended command execution or broaden process selection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
else:
        # POSIX fallback via pgrep
        try:
            out = subprocess.check_output(["pgrep", "-f", profile], text=True, timeout=10)
            pids = [int(x.strip()) for x in out.splitlines() if x.strip().isdigit()]
        except Exception:
            pass
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
Installing packages at runtime is outside the normal scope of a browser-control skill and gives the tool host-level mutation capability. In an agent environment, that can be abused to fetch and run new code paths not present in the reviewed skill, undermining trust boundaries.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
import html2text  # noqa
        return html2text
    except ImportError:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "html2text"])
        import html2text  # noqa
        return html2text
Confidence
91% confidence
Finding
The skill performs a runtime pip install on the host system, which expands its capability from browser automation into host package management and arbitrary code retrieval from package sources. In an agent setting, this can introduce supply-chain risk, environment drift, and execution of unreviewed install-time code.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The x-post command publishes content directly to a social-media account without any user confirmation, preview, or safety checkpoint. Because the skill is designed to operate logged-in sessions, an agent or prompt injection could cause irreversible public posting, reputational harm, spam, or policy violations.

Unpinned Dependencies

Low
Category
Supply Chain
Content
playwright>=1.40
html2text>=2024.2.26
requests>=2.31
psutil>=5.9
Confidence
96% confidence
Finding
The dependency specification uses a lower-bound version (playwright>=1.40) rather than pinning an exact version, which makes builds non-reproducible and allows future installs to pull in unexpected releases. In a browser-automation skill that can drive an already logged-in browser, unpredictable dependency drift increases supply-chain and stability risk, even though this file alone does not prove exploitation.

Unpinned Dependencies

Low
Category
Supply Chain
Content
playwright>=1.40
html2text>=2024.2.26
requests>=2.31
psutil>=5.9
Confidence
95% confidence
Finding
The html2text dependency is unpinned, so installations may resolve to different future versions with unreviewed behavior changes or vulnerabilities. While this package is lower risk than browser-control components, leaving it floating still weakens build integrity and reproducibility.

Unpinned Dependencies

Low
Category
Supply Chain
Content
playwright>=1.40
html2text>=2024.2.26
requests>=2.31
psutil>=5.9
Confidence
99% confidence
Finding
requests>=2.31 is unpinned, which creates supply-chain uncertainty and makes it impossible to know whether deployed environments are using a vulnerable or safe release. This matters more in a skill that may fetch pages and interact with authenticated web sessions, because HTTP client flaws can contribute to credential leakage, SSRF-like abuse patterns, or insecure transport handling depending on usage.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
The manifest does not pin requests, and the package has multiple known advisories, so the deployed version could be one of the affected releases without any way to verify safety from this file. In the context of a browser/web automation skill that may access authenticated sites and fetch remote content, uncertainty around HTTP client vulnerabilities is more concerning because it can amplify data exposure risks.

Unpinned Dependencies

Low
Category
Supply Chain
Content
playwright>=1.40
html2text>=2024.2.26
requests>=2.31
psutil>=5.9
Confidence
97% confidence
Finding
psutil>=5.9 is not pinned, so future installs may pull different versions with unknown security or reliability characteristics. Although psutil is not inherently dangerous here, unpinned system-level libraries increase operational and supply-chain risk unnecessarily.

Unverifiable Dependency: psutil has 2 known advisory(ies) (CVE-2019-18874 (Double Free in psutil); CVE-2019-18874 (psutil (aka python-psutil) through 5.6.5 can have a double free. This occurs bec)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding
The unpinned psutil dependency prevents verification of whether an affected vulnerable release may be installed. The known advisories for psutil are limited and context-dependent, so the impact is lower here, but exact version control is still necessary to reduce supply-chain uncertainty.

Static analysis

No suspicious patterns detected.