Back to skill

Security audit

跟团游搜索与推荐

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its travel-search purpose, but it embeds a reusable proxy token and sends travel queries through external proxy services.

Review before installing. The skill does not appear to read local files, install persistence, or modify accounts, but your destination, departure city, and travel date queries are sent to external proxy services. The publisher should remove and rotate the embedded proxy token and rely on a properly scoped runtime secret.

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/group_tour.py:17
Finding

Hardcoded Shared Proxy Authentication Token

Content
View full analysis

Vulnerability Details

File Location: scripts/group_tour.py, lines 15–17 and 74–108
Vulnerability Type: Hardcoded credential
Risk Level: Medium

Vulnerable Code

python
TONGCHENG_PROXY = "https://1439498936-7vqpkiipef.ap-guangzhou.tencentscf.com"
FLIGGY_PROXY = "https://1439498936-6sysdjjt99.ap-guangzhou.tencentscf.com"
PROXY_TOKEN = os.environ.get("PROXY_TOKEN", "tp_8k2mX9vQ4z")

The credential is attached to requests to both proxy services:

python
def _call_tongcheng(rtype, params):
    """调用同程SCF代理"""
    body = json.dumps(
        {"type": rtype, "params": params},
        ensure_ascii=False,
        separators=(",", ":"),
    ).encode("utf-8")
    req = urllib.request.Request(
        TONGCHENG_PROXY,
        data=body,
        headers={
            "Content-Type": "application/json",
            "X-Proxy-Token": PROXY_TOKEN,
        },
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=TC_TIMEOUT) as r:
            return json.loads(r.read().decode("utf-8"))
python
def _call_fliggy(rtype, params, timeout=None):
    """调用飞猪SCF代理"""
    body = json.dumps(
        {"type": rtype, "params": params},
        ensure_ascii=False,
        separators=(",", ":"),
    ).encode("utf-8")
    req = urllib.request.Request(
        FLIGGY_PROXY,
        data=body,
        headers={
            "Content-Type": "application/json",
            "X-Proxy-Token": PROXY_TOKEN,
        },
        method="POST",
    )
    _timeout = timeout or FG_TIMEOUT
    try:
        with urllib.request.urlopen(req, timeout=_timeout) as r:
            return json.loads(r.read().decode("utf-8"))

Technical Analysis

The implementation retrieves PROXY_TOKEN from the environment but supplies a static fallback value embedded in the distributed source code. Anyone able to obtain the Skill package can recove ...[truncated 2164 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove the hardcoded fallback value and require the credential to be provided securely at runtime:
    python
    PROXY_TOKEN = os.environ.get("PROXY_TOKEN")
    if not PROXY_TOKEN:
        raise RuntimeError("PROXY_TOKEN is required")
    
  2. Immediately revoke and rotate the exposed token, because removing it from a future release does not invalidate copies already distributed.
  3. Store replacement credentials in the platform's secret-management facility rather than source code, package metadata, logs, or command-line arguments.
  4. Use separate credentials for the Tongcheng and Fliggy proxies to reduce the impact of a single credential disclosure.
  5. Restrict each credential server-side to the minimum required request types and upstream operations.
  6. Validate request schemas and reject unknown type values or unexpected parameters at the proxy boundary.
  7. Apply per-client rate limits, quotas, expiration, rotation, anomaly monitoring, and audit logging.
  8. Where the hosting platform supports it, bind access to trusted workload identity or signed short-lived requests rather than a reusable shared token.
  9. Ensure proxy logs redact authentication headers and apply a documented retention policy to travel-search data.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

Tainted flow: 'req' from os.environ.get (line 100, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/group_tour.py (reported line 86)May include surrounding context.

python
method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=TC_TIMEOUT) as r:
            return json.loads(r.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        err = ""

Tainted flow: 'req' from os.environ.get (line 100, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/group_tour.py (reported line 107)May include surrounding context.

python
)
    _timeout = timeout or FG_TIMEOUT
    try:
        with urllib.request.urlopen(req, timeout=_timeout) as r:
            return json.loads(r.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        err = ""

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest display name, description, examples, and usage guidance are entirely in Chinese and implicitly assume Chinese-language input such as "说一句话就能找到完美线路" and example utterances like "想去海边玩". There is no indication that other languages are supported, no user opt-in for Chinese, and no documented justification for a Chinese-only locale constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The helper functions send request bodies containing user-supplied travel query parameters to external SCF proxy endpoints via HTTP POST. Although the code has internal docstrings, there is no user-facing notice, confirmation, or visible disclosure in this file that departure, destination, and date data will be transmitted to third-party proxy services.

Content

No source excerpt is available for this finding.

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/group_tour.py (reported line 97)May include surrounding context.

python
return {"error": "request error: " + str(e)}


def _call_fliggy(rtype, params, timeout=None):
    """调用飞猪SCF代理"""
    body = json.dumps({"type": rtype, "params": params}, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
    req = urllib.request.Request(

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The module description and all user-facing strings are exclusively in Chinese, which effectively constrains the skill's interaction language. There is no natural-language indication that the user may choose another language or that the locale restriction is intentional and documented as region-specific.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.