Back to skill

Security audit

goods_search

Security checks for vulnerabilities and agentic risk

Overview

The shopping-search skill is coherent, but it ships a plaintext API key and can send user queries, images, and metadata to an unvalidated service endpoint.

Review before installing. Do not ship or use the included API key; it should be revoked and replaced with a user-controlled secret. Only use the skill if you are comfortable sending shopping queries, optional images, user metadata, and location context to the configured Viking AI Search endpoint, and restrict the endpoint to the documented HTTPS hosts.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/.env:2
Finding

Hardcoded API credential committed to the skill package

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/aisearch.py:193
Finding

Attacker-controlled API endpoint can receive the bearer credential and sensitive search data

Content
View full analysis
Dict[str, str]: headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json", "Accept": accept, } if self.extra_headers: headers.update(self.extra_headers) return headers def _post_json(self, path: str, payload: Dict[str, Any]) -> Any: url = f"{self.base_url}{path}" headers = self._headers(accept="application/json") try: resp = requests.post(url, headers=headers, json=payload, timeout=self.timeout) ``` The command-line wrapper exposes the destination override directly: ```python parser.add_argument("--base-url", default="", help="覆盖 VIKING_AISEARCH_API_BASE") parser.add_argument("--api-key", default="", help="覆盖 VIKING_AISEARCH_API_KEY") ``` ```python tool = VikingAISearch( base_url=args.base_url or None, api_key=args.api_key or None, application_id=args.application_id or None, ) ``` ### Technical Analysis The destination URL is accepted from a constructor argument, command-line option, or environment variable without validating its scheme or host. The client then unconditionally attaches the API bearer credential to requests sent to that destination. Consequently, a party capable of influencing `--base-url`, `VIKING_AISEARCH_API_BASE`, or a caller of `VikingAISearch` can redirect requests to an attacker-controlled server. That server receives: - The `Authorization: Bearer ...` credential. - Search and chat text. - Session identifiers. - Optional user metadata. - Optional geographic context. - Image URLs or complete ima ...[truncated 1800 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/aisearch.py:251
Finding

HTTP error output exposes sensitive request payloads

Content
View full analysis
= 400: response_payload = raw_json if raw_json is not None else _truncate_str(raw_text) debug_detail: Dict[str, Any] = { "url": url, "path": path, "status_code": resp.status_code, "request": {"body": _redact_for_log(payload)}, "response": response_payload, } raise RuntimeError(f"HTTP {resp.status_code}", debug_detail) ``` The same behavior is present for streaming errors: ```python if resp.status_code >= 400: raw_json: Any = None raw_text: str = "" try: raw_json = resp.json() except Exception: raw_text = resp.text response_payload = raw_json if raw_json is not None else _truncate_str(raw_text) debug_detail: Dict[str, Any] = { "url": url, "path": path, "status_code": resp.status_code, "request": {"body": _redact_for_log(payload)}, "response": response_payload, } raise RuntimeError(f"HTTP {resp.status_code}", debug_detail) ``` The redaction helper removes only a small set of key names: ```python if key.lower() in {"authorization", "api_key", "apikey"}: continue ``` ### Technical Analysis On an HTTP error, the implementation places the request body and remote response into a diagnostic object. The caller subsequently includes that object in the returned error structure, and the command-line wrapper serializes the result to standard output. Although `_redact_for_log` removes fields named `authorization`, `api_key`, or `apikey`, API credentials are carried in headers and are not part of the request body. The body can still contain sensitive data under fields such as: - `input_message` and `query`. - `user`. - `context`, including location. - `session_id`. - `image_url` ...[truncated 1501 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (16)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/aisearch.py (reported line 27)May include surrounding context.

python
return None


def _load_dotenv(dotenv_path: str = ".env") -> None:
    try:
        base_dir = os.path.dirname(__file__)
        path = dotenv_path or ".env"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/aisearch.py (reported line 30)May include surrounding context.

python
return None


def _load_dotenv(dotenv_path: str = ".env") -> None:
    try:
        base_dir = os.path.dirname(__file__)
        path = dotenv_path or ".env"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill references executable scripts and external interface docs, implying file reads, environment-variable use, and network access, but it does not declare any explicit tool scope such as allowed-tools or permissions. This creates a least-privilege gap: a host agent may grant broader capabilities than are actually necessary, increasing the blast radius if the skill is misused or prompt-injected through shopping queries or retrieved content.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
79% confidence
Finding

The instruction is written as a mandatory policy — “务必优先使用本 Skill,不要试图直接回答” — and the skill’s user-facing natural-language description is entirely Chinese, with no indication that users may choose another language or locale. Under the stated policy, forcing a specific language or locale without user opt-in can be a violation.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger phrases are very broad and map to common shopping-language terms such as '搜索', '推荐', '价格', and '适合什么', which can cause the skill to activate for many ordinary conversations. Overbroad invocation increases the chance of unintended external searches, data exposure to third-party services, or bypass of safer/default handling paths when the user did not clearly request this integration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This markdown file describes sending session identifiers, user profile fields, image data, and geolocation context in API requests, but it does not warn readers that these fields may contain sensitive personal data or should be shared only with appropriate consent. For markdown files, SQP-2 applies when the skill description omits warnings about behaviors that could affect user data or privacy.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file returns multiple user-facing error and status strings exclusively in Chinese, such as at L158, L179, and throughout the class methods. This imposes a specific language on users without any evident opt-in or locale-selection mechanism, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code sends request payloads to a remote API via requests.post in both _post_json and _post_stream. The transmitted payloads can include user text, image URLs, user/context objects, and search parameters, but there is no confirmation prompt, user-facing disclosure, or explanatory comment/docstring in this file warning that these inputs are sent over the network.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/aisearch.py (reported line 241)May include surrounding context.

python
url = f"{self.base_url}{path}"
        headers = self._headers(accept="application/json")
        try:
            resp = requests.post(url, headers=headers, json=payload, timeout=self.timeout)
        except requests.exceptions.RequestException as e:
            raise RuntimeError(str(e)) from e

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/aisearch.py (reported line 269)May include surrounding context.

python
url = f"{self.base_url}{path}"
        headers = self._headers(accept="text/event-stream")
        try:
            resp = requests.post(
                url,
                headers=headers,
                json=payload,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The argument parser description, epilog, help strings, and runtime error messages are written in Chinese only, which imposes a specific language on all users without opt-in or an alternative locale. Under the language/locale policy rule, this is a natural-language policy issue because no language selection mechanism or justification is provided.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The chat path sends text, image URLs, user JSON, context JSON, and search parameters to the external Viking AI Search service, but this file provides no confirmation prompt or explicit user-facing disclosure at the point of transmission. The same concern applies to the search invocation later in the file, and the visible CLI help text describes parameters but does not warn that supplied data will be sent over the network.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

该文件全文以中文编写,且未说明这是面向特定中文使用者或区域合规场景的限定文档。根据语言/区域策略,若技能或说明强制单一语言而未提供用户选择或明确 justification,可能构成自然语言层面的策略问题。

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is specified as requests>=2.25.0, which permits installation of many future or older-compatible releases without guaranteeing a reviewed, fixed version. This weakens build reproducibility and can inadvertently pull in a vulnerable or incompatible release during deployment.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.25.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding

requests has multiple known advisories, and because the manifest does not pin the package version, there is no assurance that deployed environments will avoid affected releases. In a skill likely to interact with external services for search or shopping flows, use of a vulnerable HTTP client could expose credentials, request integrity, or sensitive data depending on the resolved version.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The script accepts an API key directly via --api-key and also references environment-based credentials when constructing the client, but there is no comment, help text, or user-facing disclosure about handling sensitive credentials. For a code-file audit under this rule, sensitive credential access without any disclosure is in scope.

Content

No source excerpt is available for this finding.