Back to skill

Security audit

TikTok官方-店铺售后

Security checks for vulnerabilities and agentic risk

Overview

This TikTok Shop skill should be reviewed before installation because it can use seller authorization through a broader proxy than its reject-reasons purpose requires.

Install only if you trust the publisher and runtime environment, and ensure this skill is constrained to the intended Get Reject Reasons flow. Avoid enabling or routing calls to return_refund_proxy.py unless you are comfortable with broader seller-API access, and do not set LINKFOX_TOOL_GATEWAY or TIKTOK_SHOP_API_BASE_URL to untrusted or non-HTTPS destinations.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/_return_refund_api_runner.py:258
Finding

Generic Authenticated Proxy Exceeds the Skill's Declared Read-Only Scope

Content
View full analysis

Vulnerability Details

File Location: scripts/_return_refund_api_runner.py:258-297, with the exposed entry point at scripts/return_refund_proxy.py:12-22 and broad path validation at scripts/_shop_return_refund_common.py:106-124
Vulnerability Type: Excessive API capability and insufficient authorization boundary enforcement
Risk Level: Medium

Vulnerable Code

python
# scripts/return_refund_proxy.py:12-22
def main() -> None:
    if len(sys.argv) < 2:
        print(
            "Usage: return_refund_proxy.py '<JSON>'\n"
            "Required: openId, path, method\n"
            "path whitelist: return_refund/, authorization/",
            file=sys.stderr,
        )
        sys.exit(1)
    params = json.loads(sys.argv[1])
    print(json.dumps(run_return_refund_proxy(params, "return_refund_proxy.py"), indent=2, ensure_ascii=False))
python
# scripts/_return_refund_api_runner.py:258-297
def run_return_refund_proxy(params: dict, caller: str = "return_refund_proxy.py") -> dict:
    """Generic proxy: path + method + openId (+ optional shop_cipher)."""
    if not params.get("skipDepCheck"):
        ensure_auth_skill_available(caller)

    path = params.get("path")
    method = params.get("method")
    if not path or not method:
        print("Missing required fields: path, method", file=sys.stderr)
        sys.exit(1)

    open_id = require_open_id(params)

    shop_cipher = None
    needs_cipher = str(path).lstrip("/").startswith("return_refund/")
    if needs_cipher:
        shop_cipher = resolve_shop_cipher(params, open_id)

    query_string = params.get("queryString")
    if shop_cipher:
        pairs = dict(parse_qsl(str(query_string or "").lstrip("?"), keep_blank_values=True))
        pairs["shop_cipher"] = shop_cipher
        query_string = urlencode(pairs)

    body = params.get("body")
    if body is not None and not isinstance(body, str):
        body = json.dumps(body, ensure_ascii=False, separators=(",", ":"))
  
...[truncated 3868 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove return_refund_proxy.py from this read-only Skill unless generic forwarding is essential.
  2. Replace namespace-prefix validation with an exact method-and-path allowlist:
    • GET authorization/202309/shops
    • GET return_refund/202309/reject_reasons
  3. Reject all methods other than GET in the current Skill.
  4. Route requests exclusively through RETURN_REFUND_ENDPOINTS so callers cannot select arbitrary paths or methods.
  5. If write operations are added later, implement each as a separate named API with:
    • Exact path and method validation.
    • A strict request schema.
    • Rejection of undocumented body and query fields.
    • Explicit user confirmation immediately before execution.
    • Clear disclosure that the operation changes seller data.
  6. Enforce the same exact allowlist at the LinkFox gateway so client-side validation is not the sole security boundary.
  7. Add automated negative tests confirming that arbitrary paths and POST, PUT, PATCH, and DELETE methods are rejected.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_shop_return_refund_common.py:15
Finding

Environment-Controlled Gateway Can Receive API Credentials and Sensitive Business Metadata

Content
View full analysis

Vulnerability Details

File Location: scripts/_shop_return_refund_common.py:15-20,59-85
Vulnerability Type: Unvalidated credential-bearing network destination
Risk Level: Medium

Vulnerable Code

python
# scripts/_shop_return_refund_common.py:15-20
API_BASE_URL = (
    os.environ.get("LINKFOX_TOOL_GATEWAY")
    or os.environ.get("TIKTOK_SHOP_API_BASE_URL")
    or "https://tool-gateway.linkfox.com"
).rstrip("/")
DEVELOPER_PROXY_ENDPOINT = f"{API_BASE_URL}/tiktokShop/developerProxy"
python
# scripts/_shop_return_refund_common.py:59-85
def get_api_key() -> str:
    key = os.environ.get("LINKFOX_AGENT_API_KEY") or os.environ.get("LINKFOXAGENT_API_KEY")
    if not key:
        print("API Key 未配置", file=sys.stderr)
        sys.exit(1)
    return key


def call_api(endpoint: str, params: dict) -> dict:
    api_key = get_api_key()
    data = json.dumps(params).encode("utf-8")
    req = Request(
        endpoint,
        data=data,
        headers={
            "Authorization": api_key,
            "Content-Type": "application/json",
            "User-Agent": "LinkFox-Skill/1.0",
            "SESSION_ID": os.environ.get("SESSION_ID", ""),
            "MODE_ID": os.environ.get("MODE_ID", ""),
            "APP_NAME": os.environ.get("APP_NAME", ""),
        },
        method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            return json.loads(response.read().decode("utf-8"))

Technical Analysis

The default destination, https://tool-gateway.linkfox.com, is consistent with the Skill's documented gateway-backed functionality. Sending an API credential and the business request to that trusted gateway is therefore necessary for normal operation.

The insecure behavior is that either LINKFOX_TOOL_GATEWAY or TIKTOK_SHOP_API_BASE_URL can replace the destination without validation. The code does not enforce:

  • HTTPS.
  • An approved hostname.
  • An approved port.
  • An explicit development mode for ...[truncated 2215 chars]
Remediation
View remediation

Remediation Suggestions

  1. Permit production requests only to an explicit allowlist such as https://tool-gateway.linkfox.com.
  2. Parse the configured URL and enforce:
    • The https scheme.
    • An approved hostname.
    • An approved port.
    • No embedded username or password.
  3. If endpoint overrides are required for testing:
    • Require an explicit development-mode flag.
    • Refuse to send production API keys to nonproduction hosts.
    • Use separate, narrowly scoped test credentials.
    • Display a clear warning identifying the destination.
  4. Determine whether SESSION_ID, MODE_ID, and APP_NAME are required by the gateway. Remove any header that is not necessary.
  5. Use a short-lived, narrowly scoped credential instead of a broadly reusable static API key where supported.
  6. Ensure credentials are rotated promptly if execution with an untrusted override is suspected.
  7. Add tests that reject HTTP URLs, unapproved domains, embedded credentials, unusual ports, and malformed destinations.
  8. Apply equivalent destination validation at deployment and orchestration layers so attackers cannot silently inject endpoint overrides.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (24)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The request target is derived from environment-controlled base URL configuration, and the same request includes sensitive credentials and session/context headers. If an attacker can influence LINKFOX_TOOL_GATEWAY or TIKTOK_SHOP_API_BASE_URL, the skill can exfiltrate the API key, SESSION_ID, MODE_ID, and APP_NAME to an attacker-controlled endpoint, which is especially relevant in an agent/plugin deployment environment where env injection is plausible.

Content

Scanner excerpt · scripts/_shop_return_refund_common.py (reported line 84)May include surrounding context.

python
method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

A generic proxy permitting arbitrary methods and paths under return_refund/ materially exceeds the stated read-only purpose and could enable write actions if the backend accepts them. In this context, the danger is increased because the skill sits behind a trusted ERP proxy with automatic token refresh, making unauthorized or unintended business actions easier once the skill is invoked.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

A generic proxy permitting arbitrary methods and paths under return_refund/ materially exceeds the stated read-only purpose and could enable write actions if the backend accepts them. In this context, the danger is increased because the skill sits behind a trusted ERP proxy with automatic token refresh, making unauthorized or unintended business actions easier once the skill is invoked.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

A generic proxy permitting arbitrary methods and paths under return_refund/ materially exceeds the stated read-only purpose and could enable write actions if the backend accepts them. In this context, the danger is increased because the skill sits behind a trusted ERP proxy with automatic token refresh, making unauthorized or unintended business actions easier once the skill is invoked.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

A generic proxy permitting arbitrary methods and paths under return_refund/ materially exceeds the stated read-only purpose and could enable write actions if the backend accepts them. In this context, the danger is increased because the skill sits behind a trusted ERP proxy with automatic token refresh, making unauthorized or unintended business actions easier once the skill is invoked.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The file documents and wires in a shop-authorization listing API, while the skill is declared to only handle Return & Refund reject-reasons. This capability mismatch violates least privilege and can cause the agent to expose or use shop enumeration functionality outside the user-expected scope, increasing the chance of unauthorized data access or confused-deputy behavior.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The generic proxy accepts caller-controlled path and method values and forwards them through the ERP developer proxy with the user's openId, only adding shop_cipher automatically for return_refund paths. This breaks the skill's declared narrow scope of reject-reason retrieval and can be abused to invoke other TikTok Shop ERP endpoints, including potentially state-changing operations if reachable through the proxy.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script exposes a get_authorized_shops action even though the skill metadata says the skill should only handle Return/Refund reject-reason retrieval and explicitly excludes authorization-related behavior. This creates a scope mismatch that can enable unauthorized capability expansion, data exposure about which shops a user can access, and bypass of policy or review assumptions based on the manifest.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares no explicit tool restrictions even though its documented operation clearly depends on shell execution, network access, and likely environment-backed credentials. Without an allowlist or permission boundary, an agent may invoke broader capabilities than users expect, increasing the blast radius if the skill is misrouted, prompt-injected, or later extended unsafely.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

By introducing shop-discovery/authorization-listing behavior that is not justified by the skill's declared purpose, the documentation expands the operational scope of the skill beyond reject-reason retrieval. In an agent setting, this can enable unnecessary access to authorized shop metadata and identifiers, which may be chained with other skills or prompts to pivot into broader account operations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
78% confidence
Finding

To fulfill requests, the code calls 'authorization/202309/shops' and inspects the caller's authorized shops to derive a shop cipher. The manifest explicitly says the skill does not include authorization, so embedding shop-list retrieval introduces a separate capability outside the stated return/refund reject-reason purpose.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill is described as a narrowly scoped return/refund reject-reason lookup, but the endpoint registry also exposes a shop-authorization discovery API that lists authorized shops and shop_cipher data. This expands the skill's reachable capability beyond its declared purpose and can enable unnecessary tenant/shop enumeration, increasing the blast radius if the agent is prompted or miswired to call unintended endpoints.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Implementing shop-authorization discovery in a skill whose stated function is only reject-reason retrieval violates least privilege and creates a scope mismatch between documented and actual behavior. In an agent setting, hidden extra capabilities are dangerous because prompt injection, routing mistakes, or tool misuse can trigger data access paths that operators and users do not expect.

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

python
print(f"DEPENDENCY_MISSING: {json.dumps(payload, ensure_ascii=False)}", file=sys.stderr)
        sys.exit(DEPENDENCY_EXIT_CODE)
    try:
        result = subprocess.run(
            [sys.executable, str(checker)],
            capture_output=True,
            text=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The helper performs an HTTP POST to an external gateway and includes SESSION_ID, MODE_ID, and APP_NAME values from environment variables in request headers. While the function is technically clear in code, this file provides no user-facing disclosure via comments, docstrings, prompts, or logging that contextual/system identifiers are being transmitted off-host.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file is labeled as return/refund functionality, but actually performs authorized-shop lookup, which is misleading and can conceal capability drift during review. In security-sensitive integrations, inaccurate labeling increases the chance that reviewers, operators, or calling agents grant trust to functionality they did not intend to expose.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script accepts an arbitrary api value from JSON input and forwards it to a generic backend runner, while the skill metadata claims this skill should only expose a single reject-reasons endpoint. That mismatch can enable unintended API reach within the return/refund surface, increasing the chance of privilege overreach or abuse if callers can invoke endpoints not covered by the manifest or expected user consent flow.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The wrapper is explicitly described and implemented as a generic path/method proxy, while the skill manifest says the skill should only support the narrow 'Get Reject Reasons' use case. This creates scope expansion: callers can potentially reach other return/refund endpoints through the shared runner, enabling unintended reads or future write-capable API access if the downstream whitelist is broad or changes.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The module documentation advertises a generic proxy, which contradicts the manifest's narrowly scoped, non-auth reject-reasons-only purpose. This mismatch is dangerous because it signals the implementation may intentionally or accidentally expose a broader API surface than reviewers, orchestrators, or users expect, undermining least privilege and increasing the chance of misuse.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The usage text states that the proxy permits 'authorization/' paths even though the skill metadata says this skill does not include authorization. Allowing auth-related path access in a non-auth skill creates an unjustified privilege boundary bypass and may expose token, shop-binding, or other sensitive authorization flows through an otherwise unrelated return/refund tool.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown file contains user-facing instructional text such as "官方文档" and other Chinese labels in the LinkFox mapping section, while the rest of the document is in English. Because the file does not offer a language/locale choice or explain that it is intended for a Chinese-speaking audience only, it may violate the language/locale policy criterion for natural-language content.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The docstring says 'Generic proxy: path + method + openId (+ optional shop_cipher)', which implies shop_cipher is merely an optional supplied parameter. In reality, for return_refund paths the code automatically invokes shop resolution logic, including an authorization/shops API call, so the inline documentation understates and misdescribes the actual behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The stderr message "API Key 未配置" is presented only in Chinese, which imposes a specific language on users without offering a choice or documenting a locale-specific requirement. This is a natural-language policy concern because the file does not indicate that the skill is intentionally limited to Chinese-speaking users.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The docstring explicitly labels this as a 'generic registered API caller,' which contradicts the manifest's representation of a narrowly scoped reject-reasons skill. On its own this is documentation-level evidence, but in context it corroborates that the implementation was designed for broader capability than advertised, which can mislead reviewers and increase the risk of unsafe exposure.

Content

No source excerpt is available for this finding.