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## 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
## 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## 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
## 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
Content
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 ""
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.

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.

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.

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.

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.

Description-Behavior Mismatch

High
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.

Description-Behavior Mismatch

High
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.

Description-Behavior Mismatch

High
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.

Lp3

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.

Context-Inappropriate Capability

Medium
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.

Context-Inappropriate Capability

Medium
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.

Description-Behavior Mismatch

Medium
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.

Context-Inappropriate Capability

Medium
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.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
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.

Intent-Code Divergence

Medium
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.

Description-Behavior Mismatch

Medium
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.

Description-Behavior Mismatch

Medium
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.

Intent-Code Divergence

Medium
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.

Context-Inappropriate Capability

Medium
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.

Natural-Language Policy Violations

Low
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.

Intent-Code Divergence

Low
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.

Natural-Language Policy Violations

Low
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.

Intent-Code Divergence

Low
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.

Static analysis

No suspicious patterns detected.