Back to skill

Security audit

极鲸云Ozon评论搜索

Security checks for vulnerabilities and agentic risk

Overview

The skill’s core Ozon review-search purpose is coherent, but it stores authentication tokens in too many local places and allows undocumented API-origin overrides that need careful review before install.

Install only if you are comfortable with this skill storing GeekBI login state locally in multiple locations and using the GeekBI API for Ozon review data. Prefer fixing or confirming token storage isolation and restricting API/auth URLs to approved HTTPS GeekBI hosts before using it with sensitive accounts or shared workspaces.

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/geekbi_auth.py:60
Finding
Bearer tokens are replicated into unnecessary Skill and workspace directories<![CDATA[ ## Vulnerability Details **File Location**: `scripts/geekbi_auth.py:60-75`, `scripts/geekbi_auth.py:372-384`, and `scripts/geekbi_auth.py:637-650` **Vulnerability Type**: Excessive credential storage and insecure secret distribution **Risk Level**: Medium ### Vulnerable Code ```python def _user_config_state_path(): return _absolute_path( user_config_path("GeekBI", appauthor=False, ensure_exists=True) / "temu-research-skill" / AUTH_FILE_NAME ) def _skill_state_path(): return _absolute_path(Path(__file__).parent.parent / AUTH_STATE_DIR / AUTH_FILE_NAME) def _workspace_state_path(): return _absolute_path(Path(os.getcwd()) / AUTH_STATE_DIR / AUTH_FILE_NAME) def _resolve_stores(): candidates = ( ResolvedStore(_user_config_state_path(), "user-config-directory"), ResolvedStore(_skill_state_path(), "skill-directory"), ResolvedStore(_workspace_state_path(), "working-directory"), ) ``` ```python def _write_state_files(stores, payload): normalized = _normalize_state(payload) errors = [] written = 0 for store in stores: try: _write_state_file(store, normalized) written += 1 except OSError as error: errors.append(f"{store.kind}: {_storage_probe_reason(error)}") if written == 0: reason = ";".join(errors) or "登录状态目录不可用" raise OSError(reason) ``` ```python def save_token(latest): latest_server = latest["servers"].get(server_key) if not isinstance(latest_server, dict): return False, False latest_pending = latest_server.get("pending") if not isinstance(latest_pending, dict): return False, False if latest_pending.get("deviceCode") != pending.get("deviceCode"): return False, False _remove_access_token(latest_server) latest_server["accessToken"] = access_token latest_server["accessTokenExpiresAt"] = now + max(0, expires_in - 30) latest_server.pop ...[truncated 2744 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store authentication state only in a dedicated user configuration or credential location. 2. Remove `_skill_state_path()` and `_workspace_state_path()` from the credential-store candidates. 3. Replace the unrelated `temu-research-skill` namespace with an Ozon-specific application namespace. 4. Prefer an operating-system credential manager or keychain for the bearer token; keep only non-sensitive metadata in JSON. 5. If file storage is unavoidable: - Create the directory with user-only permissions. - Refuse storage when secure permissions cannot be guaranteed. - Avoid silently ignoring permission-hardening failures. - Ensure backups and synchronization tools exclude the credential file. 6. Add a migration routine that: - Reads any existing token from legacy locations. - Writes it to the secure canonical store. - Securely removes all legacy copies. 7. Document token revocation and provide a command that clears every historical storage location. 8. Add tests confirming that successful authentication creates no state file under the project, Skill installation, or current working directory. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ozon_review_search.py:34
Finding
Unrestricted API-origin override permits untrusted endpoints and login-link phishing<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ozon_review_search.py:34-45`, `scripts/ozon_site_list.py:50-56`, and `scripts/geekbi_auth.py:697-715` **Vulnerability Type**: Missing destination validation for authenticated network requests **Risk Level**: Medium ### Vulnerable Code ```python def main(): parser = argparse.ArgumentParser(description="查询 Ozon 商品评论并输出 JSON") parser.add_argument("--base-url", default=DEFAULT_BASE_URL) parser.add_argument("--param", action="append", default=[], help="查询条件,格式为 名称=值") parser.add_argument("--timeout", type=float, default=45) args = parser.parse_args() try: params = parse_params(args.param) payload = authenticated_json_request( build_url(args.base_url, ENDPOINT, params), args.base_url, args.timeout ) ``` ```python def main(): parser = argparse.ArgumentParser(description="查询并解析 Ozon 站点") parser.add_argument("--country", help="国家、站点 UID 或域名;不传则返回全部") parser.add_argument("--base-url", default=DEFAULT_BASE_URL) parser.add_argument("--timeout", type=float, default=30) args = parser.parse_args() try: payload = authenticated_json_request( f"{args.base_url.rstrip('/')}{ENDPOINT}", args.base_url, args.timeout ) ``` ```python def authenticated_json_request( url, base_url, timeout, *, method="GET", body=None, headers=None, ): complete_pending_login(base_url, timeout) request_headers = _api_headers() if headers: request_headers.update(headers) authorization = _authorization_header(base_url) if authorization: request_headers["token"] = authorization request = Request( url, data=body, headers=request_headers, method=method, ) try: with urlopen(request, timeout=timeout) as response: ``` The authentication response can also provide the link shown to the user: ```python def _raise_action_if_ne ...[truncated 4081 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--base-url` from production-facing commands and use the fixed `https://openapi.geekbi.com` origin. 2. If endpoint overrides are required for development: - Gate them behind an explicit development mode. - Allowlist exact HTTPS origins. - Reject IP literals, embedded credentials, fragments, unexpected ports, and non-HTTPS schemes. 3. Parse URLs with a standard URL parser and compare normalized scheme, hostname, and port rather than using string concatenation. 4. Validate every `jumpUrl` against a separate allowlist of approved HTTPS authorization hosts before exposing it to the user. 5. Disable cross-origin redirects or verify the destination after every redirect. Never forward authentication headers to a different origin. 6. Separate production and development credential stores so a test endpoint cannot share authentication state with production. 7. Add tests covering: - HTTP URLs. - Lookalike domains. - URLs containing user information. - Cross-origin redirects. - Malicious `jumpUrl` values. - Alternate ports and malformed hostnames. 8. Clearly identify the approved authorization domain in user-facing prompts so users can verify it before proceeding. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • 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 (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill text references local scripts and external API documentation/workflows, which implies file reads, network access, and possibly shell/tool execution, yet it declares no permissions or trust boundaries. Undeclared capabilities are dangerous because they hide what the skill can cause an agent to do, reducing user visibility and making unintended external access or local resource use more likely.

Intent-Code Divergence

High
Confidence
88% confidence
Finding
The user-config storage path uses "temu-research-skill" instead of a path matching this Ozon/GeekBI skill, causing cross-skill state collision risk. That can lead to one skill reading, overwriting, or clearing another skill's authentication state if they run under the same user, producing unintended account mix-ups or token reuse across unrelated tools.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The skill defaults to the Russian Ozon site when the user does not specify a market, which can cause silent querying of the wrong locale or jurisdiction. While not directly a code-execution issue, forced defaults can lead to privacy, compliance, or data-quality problems because actions occur without explicit user opt-in.

Static analysis

No suspicious patterns detected.