Back to skill

Security audit

Multi-Cloud Docs/API Benchmark

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly performs the advertised documentation benchmark, but it asks users to configure cloud credentials it does not appear to need and its URL filtering can be bypassed to make the runner fetch unintended internal or arbitrary URLs.

Review before installing. Use it only in a sandboxed environment with restricted outbound network access, do not provide Alibaba Cloud access keys for this documentation-only benchmark unless the skill is revised to justify and use read-only credentials, and avoid running it with links supplied by untrusted parties.

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/benchmark_multicloud_docs_api.py:107
Finding
Insufficient URL Validation Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/benchmark_multicloud_docs_api.py:107-109, 157-170, 288-297, 533-537` **Vulnerability Type**: Server-Side Request Forgery (SSRF) through improper hostname validation **Risk Level**: Medium ### Vulnerable Code ```python def domain_allowed(url: str, domains: tuple[str, ...]) -> bool: low = url.lower() return any(d in low for d in domains) ``` The weak validation is applied to links supplied through command-line arguments: ```python manual = getattr(args, f"{p.key}_links", "").strip() if manual: links = [x.strip() for x in manual.split(",") if x.strip()] links = [u for u in links if domain_allowed(u, p.domains)] source_tier = "L0" confidence = "high" if links else "low" notes = ["Using user-pinned official links."] ``` Accepted URLs are subsequently fetched: ```python def classify_links(links: list[str], max_fetch: int = 3) -> dict[str, bool]: chunks = ["\n".join(links).lower()] for url in links[:max_fetch]: try: html = fetch_text(url, timeout=12).lower() except Exception: continue ``` The request function uses `urllib.request.urlopen`, which follows redirects without validating each resulting destination: ```python def fetch_text(url: str, timeout: int = 20) -> str: req = urllib.request.Request( url, headers={ "User-Agent": "Mozilla/5.0 (Codex MultiCloud Benchmark)", "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8", }, ) with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read().decode("utf-8", errors="ignore") ``` ### Technical Analysis The `domain_allowed` function searches the complete URL for an approved domain substring. It does not parse the URL or verify that the approved domain is the actual destination hostname. Consequently, a URL may contain an approved domain in its user-information, path, query string, or an attacker-co ...[truncated 2968 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every URL with `urllib.parse.urlsplit` and reject malformed URLs. 2. Permit only HTTPS unless HTTP is explicitly required and justified: ```python parsed = urllib.parse.urlsplit(url) if parsed.scheme != "https": return False ``` 3. Reject embedded credentials by requiring both `parsed.username` and `parsed.password` to be absent. 4. Compare the normalized hostname rather than searching the complete URL: ```python host = (parsed.hostname or "").rstrip(".").lower() def host_allowed(host: str, domains: tuple[str, ...]) -> bool: return any( host == domain or host.endswith("." + domain) for domain in domains ) ``` 5. Resolve the hostname and reject every loopback, private, link-local, multicast, unspecified, and reserved address using Python's `ipaddress` module. Validate all returned DNS addresses, not only the first one, to reduce DNS rebinding exposure. 6. Reject unexpected ports or maintain a narrow allowlist such as TCP 443. 7. Disable automatic redirects or install a redirect handler that validates the scheme, hostname, port, and resolved IP address for every redirect destination before following it. 8. Apply the same validation immediately before each network request rather than relying only on filtering performed earlier in the workflow. 9. Impose response-size limits and restrictive connection/read timeouts to reduce resource-exhaustion risk. 10. Where possible, route outbound traffic through an egress proxy that independently blocks loopback, private, link-local, metadata, and unauthorized external destinations. 11. Add regression tests for user-information confusion, malicious subdomains, domain names in paths and query strings, encoded hostnames, IPv4 and IPv6 literals, redirects to private addresses, alternate numeric IP representations, and DNS records resolving to restricted address ranges. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (11)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill documents execution of Python scripts that perform file writes and official-domain web discovery, but it does not declare any explicit tool scope such as allowed tools or permissions. This creates a mismatch between documented behavior and enforceable boundaries, increasing the risk that an agent executes with broader filesystem or network access than intended.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The prerequisites section asks for Alibaba Cloud credentials and references 'mutating operations' even though the skill is described as a documentation benchmarking workflow. Requesting cloud credentials without a clear operational need expands the attack surface and can mislead an agent or user into supplying sensitive access for a task that should only require public documentation retrieval.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The default prompt uses a very broad invocation phrase that can be triggered by ordinary requests to compare cloud documentation, without clear guardrails or activation boundaries. This increases the chance of unintended skill activation and scope overlap with general-purpose user queries, which can cause the agent to invoke this capability when the user did not explicitly intend to call the skill.

Vague Triggers

Medium
Confidence
87% confidence
Finding
This manifest file defines trigger keywords such as "llm platform" and "模型服务", which are generic phrases that could appear in ordinary discussion about AI services rather than specifically requesting this preset. The file does not provide exclusion conditions, negative examples, or tighter scope constraints to distinguish when the preset should or should not activate.

External Transmission

Medium
Category
Data Exfiltration
Content
"skills/platform/docs/alicloud-platform-multicloud-docs-api-benchmark/references/scoring.json"
)
GCP_DISCOVERY_APIS = "https://discovery.googleapis.com/discovery/v1/apis"
GITHUB_API_SEARCH_CODE = "https://api.github.com/search/code?q="

DEFAULT_SCORING_PROFILE = {
    "link_cap": 8,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The Accept-Language header is hard-coded to prefer en-US, then en, then zh-CN for fetched content. This imposes a locale preference in the skill behavior without opt-in, which matches the policy category for language or locale constraints.

External Transmission

Medium
Category
Data Exfiltration
Content
def alicloud_openapi_signals(product: str) -> dict[str, Any]:
    try:
        zh = fetch_json("https://api.aliyun.com/meta/v1/products.json?language=ZH_CN")
        en = fetch_json("https://api.aliyun.com/meta/v1/products.json?language=EN_US")
    except Exception:
        return {"resolved": False}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def alicloud_openapi_signals(product: str) -> dict[str, Any]:
    try:
        zh = fetch_json("https://api.aliyun.com/meta/v1/products.json?language=ZH_CN")
        en = fetch_json("https://api.aliyun.com/meta/v1/products.json?language=EN_US")
    except Exception:
        return {"resolved": False}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def alicloud_openapi_signals(product: str) -> dict[str, Any]:
    try:
        zh = fetch_json("https://api.aliyun.com/meta/v1/products.json?language=ZH_CN")
        en = fetch_json("https://api.aliyun.com/meta/v1/products.json?language=EN_US")
    except Exception:
        return {"resolved": False}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Low
Confidence
91% confidence
Finding
This code fetches content from DuckDuckGo, Google, GitHub, and Alibaba Cloud endpoints, and the queried product keyword is incorporated into those requests. While network access is central to the script's purpose, the file lacks any explicit docstring, comment, or user-facing notice that running it will transmit search terms to third-party services.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
preset_seeds = [str(x) for x in preset_seeds_raw if isinstance(x, str)]
        preset_seeds = [u for u in preset_seeds if domain_allowed(u, p.domains) or "github.com" in u.lower()]

        manual = getattr(args, f"{p.key}_links", "").strip()
        if manual:
            links = [x.strip() for x in manual.split(",") if x.strip()]
            links = [u for u in links if domain_allowed(u, p.domains)]
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.