Back to skill

Security audit

极鲸云Ozon类目搜索

Security checks for vulnerabilities and agentic risk

Overview

The skill does its stated Ozon research job, but it stores login tokens in multiple local places and can present unvalidated login/action links.

Review this skill before installing if you will authenticate to GeekBI. Prefer using only the default GeekBI endpoint, do not pass custom base URLs, inspect any login/action link before opening it, and clear or remove .geekbi/agent-auth.json copies from workspaces or skill directories when finished.

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:66
Finding
Bearer Tokens Are Replicated Across Multiple Local Trust Boundaries<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/geekbi_auth.py:66-87` - `scripts/geekbi_auth.py:371-380` - `scripts/geekbi_auth.py:637-648` **Vulnerability Type**: Excessive credential storage and cross-workspace token exposure **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)}") ``` ```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 ``` ### Technical Analysis The authentication state contains a live GeekBI bearer token. Instead of keeping that credential in one private, user-s ...[truncated 2865 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store authentication state only in a private operating-system user configuration directory or, preferably, an operating-system credential vault. 2. Remove `_skill_state_path()` and `_workspace_state_path()` from production credential storage. 3. Correct the path component from `temu-research-skill` to a unique Ozon Skill identifier. 4. Add migration logic that: - Reads an existing legacy state once. - Moves it into the canonical secure store. - Securely removes all legacy Skill-directory and workspace copies. 5. Do not mirror the complete authentication state merely to improve availability. Fail safely if the canonical secure store is unavailable. 6. On Windows and other non-POSIX systems, apply explicit user-only access-control lists rather than relying on inherited permissions. 7. Prefer storing a refresh handle or opaque session identifier in a system credential manager instead of a directly usable bearer token. 8. Ensure the `clear` operation removes every legacy copy and invalidates the server-side session where supported. 9. Document the credential location and retention period so users can audit and revoke stored authentication state. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ozon_category_info.py:37
Finding
Unvalidated Base and Action URLs Permit Insecure Transport and Phishing Flows<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/ozon_category_info.py:37-45` - The same `--base-url` pattern appears in the other Ozon query scripts. - `scripts/geekbi_auth.py:510-521` - `scripts/geekbi_auth.py:564-578` - `scripts/geekbi_auth.py:669-695` - `scripts/geekbi_auth.py:698-721` - `references/查询暂停与恢复流程.md:4-8` **Vulnerability Type**: Unvalidated security-sensitive URL and plaintext transport **Risk Level**: Medium ### Vulnerable Code ```python parser = argparse.ArgumentParser(description="查询 Ozon 类目详情、父链与历史") parser.add_argument("--cat-id", type=int, required=True) parser.add_argument("--site-id", type=int, default=1) parser.add_argument("--base-url", default=DEFAULT_BASE_URL) parser.add_argument("--timeout", type=float, default=45) args = parser.parse_args() try: params = build_params(args.cat_id, args.site_id) payload = authenticated_json_request( build_url(args.base_url, ENDPOINT, params), args.base_url, args.timeout ) ``` ```python def _raise_action_if_needed(payload): data = payload.get("data", {}) if isinstance(payload, dict) else {} jump_url = data.get("jumpUrl") if isinstance(data, dict) else None if not isinstance(jump_url, str) or not jump_url: return raise ActionRequired( response_message(payload, "请完成页面操作后继续"), jump_url, action=data.get("error") or "ACTION_REQUIRED", expires_in=int(data.get("expiresIn", 0)), ) ``` ```python def complete_pending_login(base_url, timeout): payload = _load_state() server_key = _server_key(base_url) server = payload["servers"].get(server_key) if not isinstance(server, dict): return False now = int(time.time()) changed = _clear_expired(server, now) pending = server.get("pending") if not isinstance(pending, dict): if changed: _persist_expiry_cleanup(server_key, now) return False endpoint = f"{base_ ...[truncated 4493 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the public `--base-url` option from production-facing scripts unless custom endpoints are an explicit functional requirement. 2. If custom endpoints are required, enforce an allowlist of exact trusted origins, including scheme, hostname, and port. 3. Require HTTPS for all authentication and data requests. Reject `http`, `file`, and all non-HTTPS schemes. 4. Normalize URLs before comparison and reject: - Embedded usernames or passwords. - Unexpected ports. - IP-literal destinations unless explicitly approved. - Hostnames outside the trusted GeekBI domain. 5. Validate `jumpUrl` independently before returning it to the Agent. Permit only approved HTTPS authentication or account-management origins. 6. Display the normalized destination hostname to the user and state that it originated from an external API response. 7. Disable redirects for authenticated requests or manually validate every redirect target before following it. 8. Never forward authentication headers across an origin change. 9. Bind stored authentication state to a canonical, validated origin rather than the caller's raw base-URL string. 10. Add tests covering malicious schemes, deceptive hostnames, user-info components, alternate ports, Unicode hostnames, and cross-origin redirects. 11. Update the pause-and-resume guidance so the Agent must reject untrusted operation links rather than assuming every server-supplied link is valid. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (1)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill clearly instructs use of local reference files and Python scripts that imply file reading, shell execution, and likely network access, but it declares no permissions. This creates a trust and review gap: an agent or runtime may execute capabilities the user has not been explicitly informed about, increasing the chance of unintended data access or external requests.

Static analysis

No suspicious patterns detected.