Back to skill

Security audit

Foreign Trade Leads

Security checks for vulnerabilities and agentic risk

Overview

This lead-scraping skill matches its stated purpose, but it needs review because its CSV export has unsafe filename handling and untrusted spreadsheet data handling.

Review this skill before installing. It is not showing deceptive or destructive intent, but only use it with clearly chosen search terms and regions, keep outputs in a controlled workspace, and treat the CSV as untrusted third-party data before opening or sharing it.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/get_google_maps_leads.py:83
Finding

CSV Formula Injection Through Unsanitized Google Maps Listing Data

Content
View full analysis

Vulnerability Details

File Location: scripts/get_google_maps_leads.py, lines 49–69 and 83–88
Vulnerability Type: CSV formula injection
Risk Level: Medium

Vulnerable Code

python
name = name_elem.get_attribute('data-item-title') if name_elem else ''

info = {'name': name, 'phone': '', 'address': '', 'website': '', 'email': ''}
try:
    phone = driver.find_element(By.CSS_SELECTOR, 'button[data-item-phone]')
    info['phone'] = phone.get_attribute('data-item-phone') if phone else ''
except Exception:
    pass
try:
    address = driver.find_element(By.CSS_SELECTOR, 'div[data-item-address]')
    info['address'] = address.get_attribute('data-item-address') if address else ''
except Exception:
    pass
try:
    website = driver.find_element(By.CSS_SELECTOR, 'a[data-item-website]')
    info['website'] = website.get_attribute('data-item-website') if website else ''
except Exception:
    pass
python
def save_to_csv(results, filename='leads.csv'):
    with open(filename, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=['name', 'phone', 'address', 'website', 'email'])
        writer.writeheader()
        writer.writerows(results)
    print(f"Saved {len(results)} rows to {filename}")

Technical Analysis

Business names, phone numbers, addresses, and website values are obtained from Google Maps and written directly into a CSV file without spreadsheet-oriented sanitization. These values are externally controlled because a Google Maps listing owner can influence the contents of the listing.

Python's csv.DictWriter correctly handles CSV quoting, but CSV quoting does not prevent spreadsheet formula interpretation. Spreadsheet applications may treat a cell beginning with =, +, -, or @ as a formula when the generated file is opened.

An attacker could therefore place a formula-like value in a listing field. If that listing ...[truncated 1595 chars]

Remediation
View remediation

Remediation Suggestions

Sanitize every externally sourced value before writing it to CSV. In particular:

  1. Convert values to strings and check their first non-whitespace character.
  2. Prefix values beginning with =, +, -, or @ with an apostrophe or another safe neutralizing character.
  3. Consider tab and carriage-return prefixes because some spreadsheet applications ignore leading control characters before evaluating formulas.
  4. Apply the protection to all exported columns, not only the business name.
  5. Document that the output contains untrusted third-party data.

Example hardening:

python
def sanitize_csv_cell(value):
    value = '' if value is None else str(value)
    if value.lstrip().startswith(('=', '+', '-', '@')):
        return "'" + value
    return value

def save_to_csv(results, filename='leads.csv'):
    safe_results = [
        {key: sanitize_csv_cell(value) for key, value in row.items()}
        for row in results
    ]

    with open(filename, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(
            f,
            fieldnames=['name', 'phone', 'address', 'website', 'email']
        )
        writer.writeheader()
        writer.writerows(safe_results)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/get_google_maps_leads.py:98
Finding

User-Controlled Output Filename Permits Path Traversal

Content
View full analysis

Vulnerability Details

File Location: scripts/get_google_maps_leads.py, line 98
Vulnerability Type: Path traversal and unintended file overwrite
Risk Level: Medium

Vulnerable Code

python
results = search_google_maps(driver, keyword, location, max_results)
save_to_csv(results, f"leads_{keyword.replace(' ', '_')}_{location}.csv")

The generated value is subsequently used as a filesystem path:

python
def save_to_csv(results, filename='leads.csv'):
    with open(filename, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=['name', 'phone', 'address', 'website', 'email'])
        writer.writeheader()
        writer.writerows(results)
    print(f"Saved {len(results)} rows to {filename}")

Technical Analysis

The keyword and location command-line arguments are incorporated into the output filename. Only spaces in keyword are replaced; path separators, traversal components such as .., absolute-path syntax, and platform-specific reserved characters are not removed.

The fixed leads_ prefix and .csv suffix restrict straightforward exploitation, but they do not establish a security boundary. A crafted value containing separators and traversal components can cause path resolution through existing directories. If the required intermediate directory exists, the resolved destination can leave the intended workspace or target an unintended file.

The file is opened with mode w, so an existing destination is truncated and overwritten. The operation runs with the permissions of the Python process.

Attack Path

  1. An attacker supplies or influences the product keyword or region passed to the Skill.
  2. The input includes path separators and traversal components.
  3. The script constructs the destination directly from that input.
  4. If the crafted path's intermediate directories exist, filesystem path resolution traverses outside the inte ...[truncated 934 chars]
Remediation
View remediation

Remediation Suggestions

Place all generated files in a dedicated output directory and derive the filename from strictly sanitized components:

  1. Permit only a conservative set of filename characters, such as letters, digits, underscores, and hyphens.
  2. Replace all other characters, including path separators, with underscores.
  3. Apply length limits to prevent filesystem errors and denial-of-service conditions.
  4. Resolve the final path and verify that it remains beneath the designated output directory.
  5. Consider generating a server-controlled filename rather than using raw search terms.
  6. Define an explicit overwrite policy, such as rejecting existing files or adding a unique timestamp.

Example hardening:

python
import re
from pathlib import Path

def safe_component(value, max_length=80):
    value = re.sub(r'[^A-Za-z0-9_-]+', '_', value)
    value = value.strip('_')[:max_length]
    return value or 'unknown'

output_dir = Path.cwd() / 'output'
output_dir.mkdir(parents=True, exist_ok=True)
output_dir = output_dir.resolve()

filename = (
    f"leads_{safe_component(keyword)}_"
    f"{safe_component(location)}.csv"
)
destination = (output_dir / filename).resolve()

if output_dir not in destination.parents:
    raise ValueError("Invalid output path")

save_to_csv(results, destination)
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill instructs the agent to write CSV output to the workspace and run a bundled script, but the manifest does not declare any tool scope or allowed-tools boundary. That weakens least-privilege controls and can let a caller invoke file-writing behavior without an explicit permission contract, increasing the chance of unintended data creation or broader tool use.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description includes broad sales-oriented trigger phrases like '获客' and '找客户', which can cause the skill to activate for generic prospecting requests beyond the narrower Google Maps lead-collection use case. Over-broad invocation increases the risk of unexpected scraping, data export, or command execution in contexts where the user did not clearly request this behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill explicitly exports business contact fields including phone, address, website, and email to CSV but does not warn users that potentially sensitive or regulated contact data will be collected and stored locally. In a scraping context, missing notice and consent boundaries can lead to privacy, compliance, and mishandling risks, especially if the CSV is later shared or retained insecurely.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The manifest description specifies invocation using Chinese trigger phrases only, while the rest of the file is primarily in English and does not explain that the skill is intentionally limited to Chinese-speaking users or provide alternative language options. This can violate language/locale policy when a skill imposes a specific language without user opt-in or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The browser is explicitly configured with '--lang=en-US', which imposes a specific language/locale on all runs. The file does not offer a user choice or document a region-specific reason for this constraint, which matches the locale-policy violation criteria.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.