Back to skill

Security audit

Ctrip Compare

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent travel-comparison purpose, but it asks for broad control over the user's existing browser session and local browser processes without enough containment or validation.

Review this skill carefully before installing. Use it only with a dedicated temporary browser profile or isolated browser instance, avoid running it against your everyday logged-in browser, verify every URL is a real vacations.ctrip.com page, choose a limited output folder, and save or close browser work before any restart or process-kill step.

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

T08 · Insecure Dependencies

Warning
Location
scripts/requirements.txt:1
Finding

Unpinned Playwright Dependency Creates Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: scripts/requirements.txt:1
Vulnerability Type: Unpinned third-party dependency
Risk Level: Medium

Vulnerable Code

text
playwright

The installation instructions in SKILL.md:43 also recommend installing the latest available release:

bash
pip install playwright

Technical Analysis

The project declares Playwright without an exact version or package-integrity hash. Consequently, installation results depend on whichever release the package index resolves at installation time. This makes builds non-reproducible and prevents the project from ensuring that users install the dependency version that was reviewed and tested.

Playwright executes with the privileges of the user running the Skill and controls a browser connected through the Chrome DevTools Protocol. A compromised upstream release, package-index account, or dependency resolution source could therefore execute arbitrary installation-time or runtime code with those privileges.

There is no evidence that the current playwright package is malicious. The confirmed weakness is the absence of version and integrity controls.

Attack Path

  1. An attacker compromises the upstream package, its publication account, or a package source used by the environment.
  2. The attacker publishes a malicious or altered release under a version accepted by the unconstrained requirement.
  3. A user follows the documented pip install playwright instruction or installs scripts/requirements.txt.
  4. The package manager resolves and installs the attacker-controlled release.
  5. Malicious package code executes during installation, import, or browser automation with the privileges of the user running the Skill.

Impact Assessment

Successful exploitation could allow arbitrary code execution under the installing or invoking user's operating-system account. This could expose files available to that account, ...[truncated 263 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin Playwright to an explicitly reviewed version, for example:

    text
    playwright==<reviewed-version>
    
  2. Generate a hash-locked requirements file and install it with hash enforcement:

    bash
    pip-compile --generate-hashes scripts/requirements.in
    pip install --require-hashes -r scripts/requirements.txt
    
  3. Use a trusted, explicitly configured package index and disallow unreviewed fallback indexes.

  4. Add automated dependency scanning and a controlled update process.

  5. Update SKILL.md so its installation command uses the same locked dependency file rather than installing an unconstrained latest release.

  6. Run the Skill in a minimally privileged virtual environment or container to reduce the impact of a compromised dependency.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/extract.py:31
Finding

Unvalidated User-Supplied URLs Are Opened in the Existing Browser Context

Content
View full analysis

Vulnerability Details

File Location: scripts/extract.py:31-44, 163-164, 187 and scripts/search.py:91-93, 107-108, 120
Vulnerability Type: Missing URL allowlist validation and unsafe browser-context reuse
Risk Level: Medium

Vulnerable Code

scripts/extract.py accepts URLs directly from command-line arguments, selects the first existing browser context, and navigates to each URL without validating its scheme, hostname, path, or final redirect destination:

python
async def extract_product(browser, url, day, output_dir):
    """提取单个产品的完整数据"""
    context = browser.contexts[0]
    page = await context.new_page()

    result = {
        "url": url,
        "product_id": "",
        "title": "",
        "departure_date": f"{day}号",
        "price": "",
        "score": "",
        "supplier": "",
        "itinerary": "",
        "error": ""
    }

    try:
        # 1. 打开页面(等待网络空闲,确保重定向完成)
        print(f"  [1/5] 打开页面: {url}")
        await page.goto(url, wait_until='networkidle', timeout=60000)
python
output_dir = sys.argv[2]
urls = sys.argv[3:]
python
r = await extract_product(browser, url, day, output_dir)

scripts/search.py has the same validation gap for its search-page argument:

python
parser = argparse.ArgumentParser(description='携程搜索页产品URL提取')
parser.add_argument('url', help='携程搜索列表页URL')
parser.add_argument('--max', type=int, default=100, help='最多提取产品数(默认100,上限100)')
python
context = browser.contexts[0]
page = await context.new_page()
python
await page.goto(args.url, wait_until='domcontentloaded', timeout=30000)

Technical Analysis

The Skill documentation limits input to Ctrip URLs under vacations.ctrip.com, but this restriction is not enforced by either script. Any URL accepted by Playwright can be passed to page.goto().

Both scripts connect to the first context of the user's al ...[truncated 3267 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse every input URL with urllib.parse.urlsplit() before navigation.
  2. Require all of the following:
    • Scheme exactly https.
    • Hostname exactly vacations.ctrip.com.
    • No embedded username or password.
    • Search-script paths beginning with /list/.
    • Extractor paths beginning with /tour/detail/ or /travel/detail/.
    • Product paths matching the expected product-ID pattern.
  3. Reject loopback, private-network, link-local, non-HTTP, malformed, and protocol-relative destinations.
  4. After navigation, validate page.url again to detect cross-origin redirects. Abort and close the page if the final URL is outside the allowlist.
  5. Monitor navigation and request events if subresource access must also be restricted.
  6. Avoid browser.contexts[0]. Use an isolated, temporary browser profile or a dedicated automation browser with no personal sessions.
  7. If reuse of an existing browser is unavoidable, create a separate incognito context where the CDP connection supports it and ensure it has no inherited cookies or storage.
  8. Apply the same centralized URL-validation function in both search.py and extract.py so their policies cannot diverge.
  9. Add tests covering malformed URLs, alternate ports, embedded credentials, deceptive subdomains, loopback addresses, and redirects to unapproved hosts.
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 (13)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructs forceful termination of browser processes using taskkill/pkill without an upfront warning about closing tabs, losing unsaved form data, interrupting downloads, or ending user sessions. In context, this is especially risky because browsers are likely to contain active authenticated sessions and unsaved work unrelated to the travel task.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs the agent to perform network access and write files, but it does not declare any explicit tool scope or allowed-tools boundary. That weakens least-privilege controls and makes it easier for an agent runtime to over-grant capabilities beyond what is strictly needed for travel comparison.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language description and prescribed prompts are written as if the skill operates in Chinese by default, including fixed Chinese trigger examples and user-facing wording. There is no indication that the user may choose another language or that the locale restriction is required for a region-specific compliance reason.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill goes beyond analyzing travel products by enumerating locally installed browsers and offering to kill and restart browser processes. That expands the attack surface into local system/process management, which is unnecessary for the core task and could disrupt the user's environment or be abused to manipulate active browser sessions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill includes commands to inspect the user's local browser installation and environment without a clear privacy notice or informed consent language. Even if the commands are simple, they reveal local software inventory and system details that are broader than necessary for comparing Ctrip products.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script's user-facing documentation and console messages are written exclusively in Chinese, which imposes a specific language on users without opt-in or alternative locale support. Under the stated policy, forcing a specific language without user choice is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
89% confidence
Finding

The script makes an HTTP request to a local debugging service on 127.0.0.1:9222 and then trusts the returned webSocketDebuggerUrl to attach to a browser via CDP. In this skill context, that gives the script powerful access to an already-running local browser session, which may include cookies, authenticated state, and open pages; if a malicious or unexpected service is listening on that port, the script could be bound to an unsafe target.

Content

Scanner excerpt · scripts/extract.py (reported line 180)May include surrounding context.

python
# 获取 WebSocket URL
    import urllib.request
    try:
        resp = urllib.request.urlopen('http://127.0.0.1:9222/json/version', timeout=5)
        info = json.loads(resp.read())
        ws_url = info['webSocketDebuggerUrl']
    except Exception as e:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file docstrings and all user-facing messages are written in Chinese, and _ensure_utf8_output() is explicitly used to support that output. There is no indication that the skill is region-specific or that users can opt into another language, which matches the locale-policy violation criteria.

Content

No source excerpt is available for this finding.

Internal Network Request

Medium
Category
Server-Side Request Forgery
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.

Content

Scanner excerpt · scripts/search.py (reported line 98)May include surrounding context.

python
# 连接浏览器CDP
    try:
        resp = urllib.request.urlopen('http://127.0.0.1:9222/json/version', timeout=5)
        info = json.loads(resp.read())
        ws_url = info['webSocketDebuggerUrl']
    except Exception as e:

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/extract.py (reported line 15)May include surrounding context.

python
def _ensure_utf8_output():
    """确保 stdout/stderr 使用 UTF-8 编码,兼容 Windows/Linux/macOS"""
    for name in ('stdout', 'stderr'):
        stream = getattr(sys, name)
        if hasattr(stream, 'buffer') and hasattr(stream, 'encoding'):
            try:
                stream.write('\u2713')

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/search.py (reported line 16)May include surrounding context.

python
def _ensure_utf8_output():
    """确保 stdout/stderr 使用 UTF-8 编码,兼容 Windows/Linux/macOS"""
    for name in ('stdout', 'stderr'):
        stream = getattr(sys, name)
        if hasattr(stream, 'buffer') and hasattr(stream, 'encoding'):
            try:
                stream.write('\u2713')

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This code persists scraped product data, including full itinerary text and source URL, to text files in the specified output directory. While the script logs progress, it does not explicitly warn the user that page contents will be saved to disk or describe the scope of what is being written.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency specification uses an unpinned package name ('playwright') without a fixed version, which makes builds non-reproducible and allows future installs to silently pull newer releases with different behavior or newly introduced malicious or vulnerable code. In this skill, Playwright is a high-privilege browser automation dependency, so unexpected version drift can meaningfully change runtime security posture even though the file itself contains no direct exploit.

Content

Scanner excerpt · scripts/requirements.txt (reported line 1)May include surrounding context.

text
playwright

Static analysis

No suspicious patterns detected.