Back to skill

Security audit

Volcengine ARK Web Search

Security checks for vulnerabilities and agentic risk

Overview

This web-search skill appears legitimate, but it needs review because its API-key-bearing requests can be redirected to an untrusted endpoint.

Review before installing. Use this only in an environment where ARK_API_KEY is intended for Volcengine ARK web search, avoid sensitive or regulated queries unless your policy allows sending them to Volcengine, and do not set ARK_BASE_URL or --base-url except to a trusted HTTPS Volcengine endpoint.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ark_web_search.py:44
Finding
ARK API Credential Can Be Sent to an Arbitrary Configurable Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ark_web_search.py:44-52`, `scripts/ark_web_search.py:154-164`, and `scripts/ark_web_search.py:438-443` **Vulnerability Type**: Unrestricted credential-bearing network destination **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument( "--base-url", default=os.getenv("ARK_BASE_URL", DEFAULT_BASE_URL), help="ARK base URL. Defaults to ARK_BASE_URL or %(default)s.", ) parser.add_argument( "--api-key", default=os.getenv("ARK_API_KEY"), help="ARK API key. Defaults to ARK_API_KEY.", ) ``` ```python def create_request(url: str, api_key: str, payload: dict[str, Any], stream: bool) -> urllib.request.Request: headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } if stream: headers["Accept"] = "text/event-stream" return urllib.request.Request( url=url, data=json.dumps(payload).encode("utf-8"), headers=headers, method="POST", ) ``` ```python def execute_request(args: argparse.Namespace, payload: dict[str, Any]) -> tuple[dict[str, Any], bool]: endpoint = args.base_url.rstrip("/") + "/responses" request = create_request(endpoint, args.api_key, payload, args.stream) if args.stream: return stream_response(request, args.timeout, args.format) return non_stream_response(request, args.timeout), False ``` ### Technical Analysis The Skill legitimately needs network access and must send the user’s search query to the Volcengine ARK Responses API. Sending the API key to the documented default endpoint, `https://ark.cn-beijing.volces.com/api/v3/responses`, is therefore necessary for its declared functionality. However, the destination is fully configurable through the `--base-url` argument or the `ARK_BASE_URL` environment variable. The supplied value is used without validating its scheme or hostname. The same request unconditionally includes th ...[truncated 1890 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Remove arbitrary endpoint overrides if they are not required.** Always use the documented Volcengine endpoint for normal operation. 2. **Allowlist approved destinations if endpoint customization is necessary.** Parse the URL with `urllib.parse.urlparse()` and require: - The `https` scheme. - An exact approved Volcengine hostname, such as `ark.cn-beijing.volces.com`. - An expected port or no explicit port. - No embedded username or password. - An expected API path prefix. 3. **Fail closed on invalid URLs.** Do not silently normalize or send credentials to an unrecognized destination. 4. **Separate development behavior from production behavior.** If custom endpoints are needed for local testing, require an explicit unsafe-development flag and avoid attaching a real API key by default. 5. **Protect credential-bearing requests.** Ensure that requests cannot be redirected or otherwise forwarded to an unapproved host while retaining the authorization header. 6. **Document the data boundary.** Clearly state that search queries are transmitted to Volcengine and advise users not to include secrets or private data unless permitted by their organization’s data-handling policy. 7. **Add automated security tests.** Verify that HTTP URLs, unapproved domains, malformed URLs, embedded credentials, and unexpected ports are rejected before request construction. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
You are a web research assistant. Answer in Chinese by default. "
    "When the user asks about today, recently, or the latest updates, prefer explicit dates. "
    "Return a concise summary body only. Do not add markdown headings, titles, or a sources section. "
    "Source links are handled by the caller."
)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Search the public web through Volcengine ARK Responses API.",
    )
    parser.add_argument("query", help="Search prompt to send to the ARK model.")
    parser.add_argument(
        "--model",
        default=os.getenv("ARK_MODEL", DEFAULT_MODEL),
        help="ARK model id. Defaults to ARK_MODEL or %(default)s.",
    )
    parser.add_argument(
        "--base-url",
        default=os.getenv("ARK_BASE_URL", DEFAULT_BASE_URL),
        help="ARK base URL. Defaults to ARK_BASE_URL or %(default)s.",
    )
    parser.add_argument(
        "--api-key",
        default=os.getenv("ARK_API
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares environment and network capabilities via metadata and documented behavior, but it does not explicitly scope or constrain those capabilities with a permissions or allowed-tools declaration. That can lead to overbroad execution in agent frameworks, making it easier for the skill to access sensitive environment data or perform unintended outbound requests beyond the user's expectations.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill encourages sending user queries to an external Volcengine ARK web API but does not provide a prominent user-facing warning about that data flow. Users may unknowingly submit sensitive prompts, which could expose confidential information to a third-party service and create compliance or privacy risks.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The default prompt instructs the agent to summarize results in Chinese, which overrides user language preferences unless they explicitly countermand it. This can cause unwanted behavior, reduce usability, and potentially misrepresent content if the user expects output in another language, though it is not a direct security compromise.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The embedded system prompt instructs the assistant to 'Answer in Chinese by default,' which imposes a language preference on users without asking for consent or offering a locale choice. This is a natural-language policy issue because the file hardcodes a default language behavior rather than making it user-selectable.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The documented default markdown format requires the headings `## 摘要` and `## 来源`, and the system prompt is said to enforce that layout. This imposes a specific language/locale choice in the skill's output without indicating user choice or opt-in.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The default system prompt says 'Do not add markdown headings, titles, or a sources section' and 'Source links are handled by the caller,' which implies sources are not included in the script output. However, the script itself renders a titled markdown document with a '来源'/'Sources' section via print_markdown and print_sources_only, so the documentation/prompt intent contradicts actual behavior.

Static analysis

No suspicious patterns detected.