Back to skill

Security audit

Web Scraper

Security checks for vulnerabilities and agentic risk

Overview

This is a usable web-scraping skill, but it needs Review because it broadly targets any web content and includes anti-blocking guidance such as proxies, IP rotation, and CAPTCHA services.

Review before installing. Use it only for sites and data you are authorized to scrape, avoid collecting unnecessary personal data, do not use proxy/CAPTCHA/IP-rotation guidance to bypass access controls, and treat CSV/XLSX outputs as untrusted unless formula-like values are sanitized.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/scrape_page.py:113
Finding

Spreadsheet Formula Injection in CSV and XLSX Exports

Content
View full analysis

Vulnerability Details

File Location: scripts/scrape_page.py, lines 113–117 and 135–136
Vulnerability Type: Spreadsheet formula injection
Risk Level: Medium

Vulnerable Code

python
fieldnames = list(data_list[0].keys())
with open(output_path, 'w', encoding='utf-8', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(data_list)
python
# Data rows
for item in data_list:
    ws.append([item.get(h, '') for h in headers])

Technical Analysis

Data extracted from arbitrary websites is stored in data_list and written directly into CSV or XLSX cells without neutralizing spreadsheet formula prefixes. A malicious website can supply a scraped value beginning with characters interpreted by spreadsheet applications as formulas, including =, +, -, or @. Tabs and carriage returns may also be relevant formula-triggering prefixes in some spreadsheet applications.

The CSV writer correctly handles delimiters and quoting, but CSV quoting does not prevent spreadsheet software from evaluating cell contents as formulas. In the XLSX branch, openpyxl can serialize a string beginning with = as a formula cell rather than literal text.

Attack Path

  1. An attacker publishes content on a page that the user is expected to scrape.
  2. The attacker places a formula-like string in a selected field, such as a title, price, or description.
  3. agent-browser extracts the attacker-controlled text.
  4. scrape_page() stores the value in the result dictionary without spreadsheet-safe encoding.
  5. save_data() passes the value directly to csv.DictWriter.writerows() or openpyxl's ws.append().
  6. The user opens the generated CSV or XLSX file in spreadsheet software.
  7. Depending on the spreadsheet application and its security configuration, the formula may be evaluated or may prompt the user to enable external content.

...[truncated 743 chars]

Remediation
View remediation

Remediation Suggestions

Treat all scraped values as untrusted before exporting them to spreadsheet-compatible formats.

  1. Create a centralized sanitization function that converts values to strings and detects formula-triggering prefixes after accounting for leading tabs, carriage returns, and similar whitespace.
  2. For CSV output, prefix formula-like values with a single apostrophe so spreadsheet applications treat them as literal text. Apply this transformation to both field names and cell values.
  3. For XLSX output, create cells explicitly as string cells and store sanitized literal values rather than allowing openpyxl to infer formula types.
  4. Consider offering a strict export mode that rejects formula-like values instead of modifying them.
  5. Document that exports contain untrusted website content and should not be opened with active content enabled.
  6. Add regression tests for values beginning with =, +, -, @, tab, and carriage return, including values with leading whitespace and quoted CSV fields.
  7. Verify the generated XLSX package to ensure attacker-controlled values are represented as strings rather than formula elements.

Example defensive helper:

python
def spreadsheet_safe(value):
    if value is None:
        return ""
    value = str(value)
    probe = value.lstrip("\t\r")
    if probe.startswith(("=", "+", "-", "@")):
        return "'" + value
    return value

Apply the helper to every header and data value before calling writerows() or ws.append().

Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code substantially matches part of the description: it uses browser automation via an external agent-browser command, extracts structured fields from web content, supports simple dynamic-content waiting, and exports to CSV/JSON/XLSX. However, the declared description claims support for pagination and infinite scroll, which are not implemented anywhere in this code chunk. The script only opens one page, extracts fields once, and saves a single-row result. Because the description presents broader scraping capabilities than the code actually provides, this is a description/behavior mismatch.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill advertises shell execution and file-writing behavior through its examples and described scripts, but it does not declare any explicit tool scope such as allowed-tools or permissions. That creates an authorization gap where an agent may invoke filesystem or shell capabilities without clear least-privilege boundaries or operator visibility.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger language says the skill should be used for scraping 'any web content,' which is overly broad and can cause automatic invocation in contexts involving sensitive, personal, copyrighted, or restricted data. In agentic systems, imprecise routing increases the chance of inappropriate use without user intent being carefully validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The documentation encourages scraping contacts, job boards, directories, and similar sources but does not clearly warn that the skill may collect and export personal data or other sensitive content. Without strong safeguards, users may unintentionally harvest PII and save it to CSV/JSON/XLSX outputs, creating privacy, compliance, and data-handling risks.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The anti-scraping section explicitly recommends proxies, IP rotation, CAPTCHA solving services, and other evasion tactics that go beyond ordinary data extraction. This materially increases the risk that the skill will be used to bypass access controls, defeat abuse protections, or facilitate unauthorized large-scale collection from third-party sites.

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/scrape_page.py (reported line 23)May include surrounding context.

python
"""Run agent-browser command and return output."""
    cmd = ["agent-browser"] + args
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,

Unbounded Output

Medium
Category
Output Handling
Confidence
75% confidence
Finding

Output size or generation rate is not bounded. Unbounded output enables denial-of-service through resource exhaustion, log flooding, or context-window stuffing.

Content

Scanner excerpt · scripts/scrape_page.py (reported line 140)May include surrounding context.

python
# Auto-fit columns (basic)
                for col in ws.columns:
                    max_length = 0
                    column = col[0].column_letter
                    for cell in col:
                        if cell.value:

Static analysis

No suspicious patterns detected.