Back to skill

Security audit

qa-browser-tester

Security checks for vulnerabilities and agentic risk

Overview

This QA skill does what it claims, but it can install software, disable browser isolation, click and submit real app controls, and expose screenshots without enough user control or warnings.

Install only if you intend to run active end-to-end tests in an authorized, disposable staging environment. Do not use it against production, third-party, admin, financial, or sensitive authenticated sessions unless you add strict allowlists, a dry-run mode, confirmation before submissions/clicks, safer dependency setup, sandboxed non-root browser execution, and private handling or cleanup of screenshots.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
references/test-phases.md:145
Finding
Unrestricted Automation Can Trigger Destructive Application Actions<![CDATA[ ## Vulnerability Details **File Location**: `references/test-phases.md:145-160`, `references/test-phases.md:214-223`, `references/test-phases.md:278-289`, and `references/test-phases.md:309-320` **Vulnerability Type**: Uncontrolled state-changing browser automation **Risk Level**: High ### Vulnerable Code ```python for i, btn in enumerate(btns): try: text = (btn.inner_text().strip() or btn.get_attribute("value") or btn.get_attribute("aria-label") or f"btn_{i}") print(f" → Clicking: '{text}'") btn.scroll_into_view_if_needed() btn.click(timeout=4000) page.wait_for_timeout(1500) shot(page, f"btn_{text[:20]}") after_url = page.url if after_url != current_url: note("ok", f"button/{text}", f"navigated to: {after_url}") page.go_back() page.wait_for_load_state("networkidle") else: note("ok", f"button/{text}", "action on same page") ``` ```python submit = form.query_selector( "button[type=submit], input[type=submit], button:last-of-type" ) if submit: try: submit.click() page.wait_for_timeout(2000) shot(page, f"form_{fi}_submitted") note("ok", f"form_{fi} on {page_url}", f"submitted → {page.url}") ``` ```python try: page.click("button[type=submit], input[type=submit]", timeout=3000) page.wait_for_timeout(2000) shot(page, "journey_04_register_result") note("ok", "journey/register", f"submitted → {page.url}") except: note("warn", "journey/register", "could not submit register form") ``` ```python try: page.click("button[type=submit], input[type=submit]", timeout=3000) page.wait_for_timeout(2000) shot(page, "journey_07_login_result") note("ok", "journey/login", f"submitted → {page.url}") except: note("warn", "journey/login", "could not submit login form") ``` ### Technical Analysis The test workflow clicks ...[truncated 1373 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require an explicit staging or disposable test environment by default. - Add a dry-run mode that inventories controls without activating them. - Enforce approved-origin, route, HTTP-method, and action allowlists. - Deny actions whose labels or accessible names indicate destructive behavior, including Delete, Remove, Purchase, Pay, Transfer, Send, Publish, Reset, Disable, and Confirm. - Require explicit operator confirmation before every potentially state-changing action. - Intercept requests and block `POST`, `PUT`, `PATCH`, and `DELETE` operations unless specifically authorized. - Use dedicated low-privilege test accounts and disposable test data. - Do not assume that browser navigation can roll back completed operations. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:82
Finding
Chromium Security Sandbox Is Unconditionally Disabled<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:82-105`, `references/docker-setup.md:17-28`, and `references/test-phases.md:21-27` **Vulnerability Type**: Unsafe browser isolation configuration **Risk Level**: High ### Vulnerable Code ```python DOCKER_ARGS = [ "--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu", "--disable-setuid-sandbox", "--single-process", ] with sync_playwright() as p: browser = p.chromium.launch(headless=True, args=DOCKER_ARGS) ``` The Docker reference additionally mandates: ```python DOCKER_ARGS = [ "--no-sandbox", # disables Chrome sandbox (not needed inside Docker) "--disable-dev-shm-usage", # use /tmp instead of /dev/shm (prevents OOM crashes) "--disable-gpu", # no GPU available in container "--disable-setuid-sandbox",# required when running as root "--single-process", # more stable in memory-constrained containers ] ``` ### Technical Analysis The Skill directs the browser to run with both the Chromium sandbox and setuid sandbox disabled. It states that these flags must be used on any Linux server or Docker container, even though containerization alone does not necessarily provide a sufficient browser security boundary. The browser processes attacker-controlled web content. Disabling sandboxing removes a major containment layer intended to restrict the effect of renderer or browser exploitation. This is particularly dangerous where setup commands and browser processes run as root. ### Attack Path 1. The Skill launches Chromium with the sandbox disabled. 2. The target application, a discovered cross-origin link, or compromised third-party content serves malicious browser content. 3. The content exploits a Chromium vulnerability. 4. Because browser sandboxing is disabled, the exploit gains the operating-system privileges of the Chromium process with fewer containment barriers. 5. The attacker accesses resources available to the Agent u ...[truncated 439 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Keep Chromium sandboxing enabled by default. - Run Chromium as a dedicated non-root user. - Use an explicitly disposable and hardened browser container when sandbox disabling is unavoidable. - Drop Linux capabilities and apply seccomp, AppArmor, or SELinux confinement. - Use a read-only root filesystem with narrowly scoped writable temporary directories. - Do not mount host sockets, credentials, home directories, or other sensitive paths into the browser container. - Restrict outbound network access to approved target origins. - Make sandbox disabling an explicit operator-approved exception rather than a mandatory global setting. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:53
Finding
Mutable External Installers and Unpinned Dependencies Are Executed at Runtime<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:53-75` and `references/docker-setup.md:33-62` **Vulnerability Type**: Remote payload execution and dependency supply-chain exposure **Risk Level**: High ### Vulnerable Code ```bash apt-get update -qq && apt-get install -y python3-pip curl -qq pip3 install playwright python3 -m playwright install chromium python3 -m playwright install-deps chromium ``` ```bash apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont python3 py3-pip pip3 install playwright export PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=$(which chromium-browser || which chromium) python3 -m playwright install chromium ``` ```bash curl https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py python3 /tmp/get-pip.py pip3 install playwright python3 -m playwright install chromium python3 -m playwright install-deps chromium ``` The Docker-specific instructions also include: ```bash npx playwright install chromium ``` ### Technical Analysis The Skill downloads a Python bootstrap script from an external URL and immediately executes it without a pinned digest, signature verification, or content inspection. The effective code can therefore change after the Skill package has been reviewed. The Playwright package is installed without an exact version or hash. The `npx` workflow can also resolve or download mutable package content at runtime. These practices make execution dependent on current upstream repository state and expose the environment to package compromise, account takeover, malicious releases, and unexpected dependency changes. ### Attack Path 1. The Skill determines that required tooling is absent. 2. It downloads `get-pip.py`, resolves an unpinned Playwright package, or invokes `npx`. 3. An upstream source, package owner, distribution channel, or dependency is compromised or publishes a malicious version. 4. The mutable content is downloaded during Skill execution. 5. Installer or package lifecycle code ex ...[truncated 568 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use a reviewed, prebuilt browser-testing image rather than installing tools during each run. - Pin Playwright and all transitive dependencies to exact versions. - Use lockfiles and require cryptographic package hashes. - Download installers only from controlled sources and verify a pinned SHA-256 digest or trusted signature before execution. - Avoid executing `get-pip.py` directly from a mutable remote endpoint. - Prevent implicit `npx` downloads; require a locally installed, locked package. - Pin browser binaries and verify their integrity. - Use an internal package mirror or artifact registry with provenance and malware scanning. - Perform installation as a non-root user in a disposable build stage. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/test-phases.md:100
Finding
Discovered Navigation Links Are Followed Without Origin or Network Restrictions<![CDATA[ ## Vulnerability Details **File Location**: `references/test-phases.md:100-122` **Vulnerability Type**: Unrestricted browser navigation **Risk Level**: Medium ### Vulnerable Code ```python for i, item in enumerate(nav): href = item.get("href", "") text = item.get("text", f"item_{i}") or f"item_{i}" if not href or href in visited_urls or href.startswith("mailto") or href == "#": continue visited_urls.add(href) if safe_goto(page, href, f"nav/{text}"): shot(page, f"nav_{i}_{text[:20]}") note("ok", f"nav/{text}", f"loaded: {page.url}") # Check for sub-menus on this page sub_links = page.eval_on_selector_all( "[class*='dropdown'] a, [class*='submenu'] a", "els => els.map(e => ({text: e.innerText.trim(), href: e.href}))" ) for j, sub in enumerate(sub_links): sub_href = sub.get("href", "") if sub_href and sub_href not in visited_urls and not sub_href.startswith("mailto"): visited_urls.add(sub_href) if safe_goto(page, sub_href, f"sub/{sub.get('text','')}"): shot(page, f"nav_{i}_sub_{j}_{sub.get('text','')[:15]}") ``` ### Technical Analysis The script follows absolute URLs extracted from application-controlled markup without validating the URL scheme, hostname, port, resolved IP address, or relationship to `BASE_URL`. Excluding only `mailto:` and `#` links does not prevent cross-origin navigation or access to localhost, private, link-local, or cloud metadata networks. Because the browser runs inside the Agent environment, it may have network access that external users do not. A target page can therefore direct the automation toward unintended services. ### Attack Path 1. An application page contains a navigation or submenu link to an attacker-selected URL. 2. The Skill extracts the resolved `href`. 3. The link passes the limited checks because it is not empty, `mailto:`, or `#`. ...[truncated 722 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Parse each URL and permit only `https` or explicitly approved `http` destinations. - Require the normalized hostname and port to match `BASE_URL` unless the operator provides a separate allowlist. - Resolve hostnames and reject loopback, private, link-local, multicast, and reserved IP ranges. - Revalidate every redirect target rather than validating only the initial URL. - Block non-web schemes such as `file:`, `data:`, `javascript:`, and custom protocols. - Apply egress firewall rules so browser restrictions are backed by network-level enforcement. - Stop testing when navigation leaves the approved origin instead of scanning the destination. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/test-phases.md:31
Finding
Full-Page Screenshots Can Be Copied Into a Web-Accessible Directory Without Redaction<![CDATA[ ## Vulnerability Details **File Location**: `references/test-phases.md:31-40` and `references/test-phases.md:428-430` **Vulnerability Type**: Insecure handling of potentially sensitive test artifacts **Risk Level**: Medium ### Vulnerable Code ```python def shot(page, name): clean = name.replace(" ", "_").replace("/", "_")[:40] path = f"{DIR}/{shot_count[0]:03d}_{clean}.png" shot_count[0] += 1 try: page.screenshot(path=path, full_page=True) print(f" 📸 {os.path.basename(path)}") except Exception as e: print(f" ⚠️ screenshot failed: {e}") ``` The post-test instructions state: ```bash # Copy to a web-accessible location if needed cp /tmp/qa_screenshots/*.png /var/www/html/qa/ ``` ### Technical Analysis The workflow captures full-page screenshots throughout form filling, registration, login, and application navigation. It does not mask password inputs, tokens, personal information, account details, or other sensitive elements. Artifacts are placed in a predictable shared temporary path without explicit restrictive permissions or automatic deletion. The documentation then recommends copying every screenshot into a web-accessible directory without requiring authentication, authorization, redaction, or web-server access controls. ### Attack Path 1. The test navigates to a page containing confidential application information. 2. `shot` captures the entire rendered page. 3. The image is stored in `/tmp/qa_screenshots`. 4. An operator follows the documented command and copies all images to `/var/www/html/qa/`. 5. If that directory is publicly served or insufficiently protected, an unauthorized party requests the screenshot files. 6. Sensitive information displayed during the test is disclosed. ### Impact Assessment Potentially exposed information includes personal data, account information, internal application content, test credentials visible in page controls, and secrets rendered by the application. Loc ...[truncated 205 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create artifacts in a unique private directory with permissions set to `0700`. - Set screenshot files to owner-only access where supported. - Mask password, token, payment, personal-data, and other sensitive elements before capture. - Disable screenshots on authenticated or sensitive pages by default. - Define a short retention period and securely delete artifacts after report generation. - Never copy test artifacts directly to a web root. - If remote sharing is necessary, use an authenticated artifact service with authorization, encryption, expiration, and audit logging. - Require the operator to review and approve each artifact before publication. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Vague Triggers

High
Confidence
97% confidence
Finding
The trigger phrases are broad enough to match common requests like 'check if everything works' or 'browse the website automatically,' causing the skill to activate in many benign contexts. Because the skill performs exhaustive interaction and can modify the host by installing software, overbroad triggering materially increases the chance of unintended execution.

Missing User Warnings

High
Confidence
97% confidence
Finding
This script does not merely observe application behavior; it fills forms, submits them, creates accounts, attempts login/logout flows, and clicks arbitrary buttons. Without a prominent warning, a user may run it against production or third-party systems and unintentionally trigger state-changing actions, data creation, emails, purchases, or destructive workflows.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill advertises exhaustive clicking, form filling, and full user-journey simulation without warning that this can submit data, trigger side effects, alter application state, or interact with production systems. In context, the absence of a user-facing warning makes accidental destructive testing more likely.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill instructs collection of broad host-environment details before QA begins, including OS, container status, user identity, package managers, installed runtimes, browser binaries, disk state, and outbound connectivity. This exceeds the minimum needed for app testing and creates unnecessary system reconnaissance that could expose sensitive infrastructure details or normalize host inspection in response to ordinary QA requests.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The skill includes package installation, dependency bootstrapping, and fetching remote installer code to modify the host environment. For a QA skill, this is dangerous because a routine testing request can trigger persistent system changes, external downloads, and execution of unpinned software, increasing supply-chain and environment-integrity risk.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The instructions perform apt/apk installs, pip installs, Playwright browser downloads, and even bootstrap pip from the internet, yet provide no explicit warning that the host will be modified. This omission is particularly dangerous because users invoking a QA skill may not expect privileged package operations or network retrieval of executable dependencies.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script captures full-page screenshots throughout navigation and testing, but the documentation does not warn that these artifacts may contain credentials, personal information, session state, or confidential application content. Combined with later instructions to copy artifacts elsewhere, this increases privacy and integrity risk because sensitive data may be retained or redistributed without operator awareness.

Context Window Stuffing

Medium
Category
Memory Poisoning
Content
name = inp.get_attribute("name") or inp.get_attribute("placeholder") or "field"
                        inp.fill(f"Test {name}")
                except Exception as e:
                    print(f"    ⚠️ Could not fill input: {e}")

            shot(page, f"form_{fi}_filled")
Confidence
85% confidence
Finding
Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The documentation explicitly instructs copying screenshots generated during QA runs into a web-served directory. Because those screenshots may contain authenticated pages, personal data, admin views, or internal application state, publishing them via a web-accessible path can cause unintended data exposure unrelated to the testing function itself.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The skill stores screenshots locally without warning that captures may contain sensitive application data, credentials, personal information, or internal UI state. While lower severity than the installation and reconnaissance issues, silent artifact creation can still create privacy and retention risks.

Missing User Warnings

Low
Confidence
75% confidence
Finding
This markdown file provides commands that install packages, install browser dependencies, and export environment variables, which can modify the container environment. The file presents these actions as operational steps but does not include a user-facing warning about their impact on the system state.

Context-Inappropriate Capability

Low
Confidence
80% confidence
Finding
The manifest describes a skill for browser-based end-to-end QA testing. While Docker-specific browser flags are clearly in scope, this section recommends running `subprocess.run(['free', '-m'])` to inspect system memory, which adds a host-level command execution capability beyond direct browser automation. That capability is only tangentially related to QA and is not declared in the manifest description itself.

Static analysis

No suspicious patterns detected.