Back to skill

Security audit

极鲸云Ozon关键词搜索

Security checks for vulnerabilities and agentic risk

Overview

The skill is for Ozon keyword research, but it stores login tokens in multiple plaintext locations and allows authenticated requests to be redirected to arbitrary API hosts.

Review this skill before installing. It appears purpose-built for GeekBI Ozon research rather than deceptive, but you should be comfortable with it saving GeekBI bearer tokens locally, including in the skill and current workspace directories, and you should avoid using any non-default or HTTP base URL unless it is an explicitly trusted GeekBI endpoint. Prefer a version that stores credentials in one protected user location and validates API destinations.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/geekbi_auth.py:59
Finding
Bearer Tokens Are Persisted in Multiple Plaintext Locations and a Cross-Skill Namespace<![CDATA[ ## Vulnerability Details **File Location**: `scripts/geekbi_auth.py:59-75`, `scripts/geekbi_auth.py:376-388`, `scripts/geekbi_auth.py:631-648` **Vulnerability Type**: Plaintext credential persistence and excessive credential replication **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) ``` The same state is written to every usable store: ```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) ``` The persisted payload includes the bearer token itself: ```python token_data = response["data"] access_token = token_data.get("accessToken") if not isinstance(access_token, str) or not access_token: raise ValueError("登录令牌响应缺少 accessToken") expires_in = int(token_data.get("expiresIn", 0)) 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_ser ...[truncated 2634 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store authentication state in exactly one per-user configuration location by default. 2. Correct the namespace from `temu-research-skill` to a unique Ozon-specific identifier such as `ozon-keyword-search-skill`. 3. Do not mirror bearer tokens into the installed Skill directory or current working directory. 4. Use an operating-system credential service, such as Keychain, Credential Manager, or Secret Service, to protect the token at rest. 5. If file storage is unavoidable: - Retain only short-lived tokens. - Enforce owner-only ACLs on every supported operating system. - Refuse storage when secure permissions cannot be established. - Store non-sensitive metadata separately from credentials. 6. Provide migration logic that securely removes legacy copies from all three old locations. 7. Ensure logout and expiration cleanup remove every legacy credential copy. 8. Restrict server-side tokens to the minimum Ozon API scopes and short lifetimes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ozon_keyword_search.py:42
Finding
Caller-Controlled Base URL Permits Authentication and Sensitive Requests to Untrusted or Cleartext Origins<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ozon_keyword_search.py:42-50`, `scripts/ozon_keyword_info.py:42-49`, `scripts/ozon_site_list.py:51-56`, `scripts/geekbi_auth.py:578-579`, `scripts/geekbi_auth.py:698-719` **Vulnerability Type**: Missing destination and transport validation for authenticated requests **Risk Level**: Medium ### Vulnerable Code Each command accepts an unrestricted base URL: ```python 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 ) ``` The authentication endpoint is constructed directly from that value: ```python endpoint = f"{base_url.rstrip('/')}{TOKEN_ENDPOINT}" try: response = _post_json(endpoint, {"deviceCode": pending["deviceCode"]}, timeout) ``` The request helper attaches a token without validating HTTPS, the hostname, or same-origin correspondence between `url` and `base_url`: ```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: ``` ### Technical Analysis Network access and bearer authentication are necessary for the declared keyword-query functionality. The vulnerability is not the normal transmission to `https://openapi.geekbi.com` ...[truncated 2761 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--base-url` from normal user-facing commands unless custom deployments are a documented requirement. 2. If custom endpoints are required, validate them against an explicit administrator-controlled allowlist. 3. Require `https` and reject URLs containing user information, fragments, unexpected ports, or non-empty paths. 4. Normalize URLs before using them as token-store keys. 5. Inside `authenticated_json_request()`, parse both `url` and `base_url` and require identical scheme, hostname, and effective port before attaching credentials. 6. Validate authentication `jumpUrl` values against an approved HTTPS domain list before presenting them to users. 7. Disable cross-origin redirects for credential-bearing requests, or strip authentication headers and reject the redirect. 8. Consider certificate pinning or enterprise CA configuration where the threat model requires protection against endpoint impersonation. 9. Add tests covering HTTP rejection, alternate-host rejection, malformed URLs, cross-origin redirects, and mismatched `url`/`base_url` values. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Dependency Installation Is Not Fully Reproducible or Integrity-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1` **Vulnerability Type**: Unhashed dependency range **Risk Level**: Low ### Vulnerable Code ```text platformdirs>=4.0,<5.0 ``` ### Technical Analysis The project depends on the legitimate `platformdirs` package, but permits any release from version 4.0 up to, but not including, version 5.0. No exact version or package hash is specified. This does not establish that the current dependency is malicious. However, it means two installations performed at different times may resolve to different package artifacts. If an allowed future release or the package distribution channel is compromised, installation can introduce code that was not included in this audit. Because `platformdirs` is imported by the authentication component, compromised dependency code would execute in the Skill process and inherit its access to authentication files, environment data, and network operations. ### Attack Path 1. A malicious or compromised release of `platformdirs` is published within the accepted `4.x` range, or the configured package index is compromised. 2. A user installs the requirements without a lock file or hash verification. 3. The package manager resolves the compromised artifact because it satisfies `>=4.0,<5.0`. 4. Malicious package initialization code executes when `geekbi_auth.py` imports `platformdirs`. 5. The dependency can act with the same operating-system privileges as the Skill process, including attempting to read persisted authentication state. ### Impact Assessment In a successful supply-chain compromise, attacker code would execute with the privileges of the user running the Skill. It could access readable files, authentication state, query data, and available network resources. The practical likelihood is lower than the coding issues above because exploitation depends on compromise of the package, package index, or installation environment. The package name itself does not appe ...[truncated 56 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `platformdirs` to a reviewed exact version. 2. Generate a lock file or fully pinned requirements file with cryptographic hashes. 3. Install with hash enforcement, such as `pip install --require-hashes`. 4. Use a trusted package index and prevent fallback to unapproved indexes. 5. Add automated dependency vulnerability and provenance scanning. 6. Review and deliberately update the pinned artifact rather than automatically accepting every future `4.x` release. ]]>
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
84% confidence
Finding
The skill instructions explicitly reference running local scripts and reading multiple local reference files, which implies file-read, shell execution, and likely network access, yet no permissions are declared. This creates a transparency and control gap: an agent or platform may perform sensitive actions users and reviewers cannot clearly audit or constrain, increasing the risk of unintended data access or external requests.

Static analysis

No suspicious patterns detected.