Back to skill

Security audit

pugoing-smart

Security checks for vulnerabilities and agentic risk

Overview

The skill appears intended for Pugoing smart-device control, but it exposes an overbroad authenticated HTTP client that can leak the API key or affect physical devices without clear guardrails.

Review before installing. Use only a tightly scoped Pugoing API key, avoid supplying full URLs or custom destinations, prefer HTTPS except for trusted local loopback testing, and require explicit user confirmation before commands that change device state.

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

Error
Location
client.py:33
Finding
API Key Disclosure Through Arbitrary Request Destinations## Vulnerability Details **File Location**: `client.py:33-36`, `client.py:49-55`, and `client.py:126-130` **Vulnerability Type**: Credential disclosure caused by insufficient destination validation **Risk Level**: High ### Vulnerable Code ```python def resolve_url(spec): url = spec.get("url") if url: return url path = spec.get("path") if not path: die("request spec requires either 'path' or 'url'") base_url = os.getenv("PUGOING_BASE_URL", "http://127.0.0.1:8080").rstrip("/") if not path.startswith("/"): path = "/" + path return base_url + path ``` ```python def build_headers(spec): headers = {"Accept": "application/json"} headers.update(spec.get("headers") or {}) api_key = os.getenv("PUGOING_API_KEY", "").strip() if api_key and "X-API-Key" not in headers: headers["X-API-Key"] = api_key return headers ``` ```python try: with urllib.request.urlopen(req, timeout=timeout) as resp: content_type = resp.headers.get("Content-Type", "") if "text/event-stream" in content_type or final_url.endswith("/api/ai/chat"): payload = collect_sse_response(resp) else: payload = parse_json_or_text(resp.read()) ``` ### Technical Analysis The request specification may provide a complete URL through the `url` field. `resolve_url()` accepts that URL without validating its scheme, hostname, port, or relationship to `PUGOING_BASE_URL`. Independently, `build_headers()` automatically adds the secret from `PUGOING_API_KEY` to every request unless an `X-API-Key` header was already supplied. Consequently, the credential is attached even when the selected destination is unrelated to the configured Pugoing server. This violates credential scoping requirements: service credentials must only be released to an explicitly trusted origin. The client also relies on the standard URL ope ...[truncated 1267 chars]
Remediation
## Remediation Suggestions 1. Remove support for arbitrary complete URLs unless it is strictly required. 2. Resolve all requests relative to `PUGOING_BASE_URL`. 3. If complete URLs must remain supported, parse both URLs with `urllib.parse.urlsplit()` and require an exact match of the trusted scheme, normalized hostname, and effective port. 4. Permit only `https` destinations, except for explicitly validated loopback development endpoints. 5. Reject URLs containing user information, unexpected fragments, unsupported schemes, or ambiguous host representations. 6. Add `X-API-Key` only after the final destination has passed origin validation. 7. Disable redirects or implement a redirect handler that rejects every cross-origin redirect. 8. Consider maintaining an explicit hostname allowlist for deployments that use multiple trusted API endpoints. 9. Add tests confirming that attacker-controlled external URLs and cross-origin redirects never receive the API key.

T09 · Insecure Skill Coding Practices

Warning
Location
client.py:42
Finding
API Credentials Can Be Transmitted Over Plaintext HTTP## Vulnerability Details **File Location**: `client.py:42-55` and `SKILL.md:11-20` **Vulnerability Type**: Plaintext transmission of sensitive authentication data **Risk Level**: Medium ### Vulnerable Code ```python base_url = os.getenv("PUGOING_BASE_URL", "http://127.0.0.1:8080").rstrip("/") if not path.startswith("/"): path = "/" + path return base_url + path ``` ```python def build_headers(spec): headers = {"Accept": "application/json"} headers.update(spec.get("headers") or {}) api_key = os.getenv("PUGOING_API_KEY", "").strip() if api_key and "X-API-Key" not in headers: headers["X-API-Key"] = api_key return headers ``` The documentation promotes an HTTP configuration and indicates that the base URL may be a LAN address: ```bash export PUGOING_BASE_URL="http://127.0.0.1:8080" export PUGOING_API_KEY="xq_agent_xxx" export PUGOING_TIMEOUT="30" ``` ### Technical Analysis The client does not enforce transport-layer encryption before adding the API key to the request. Although the default destination is loopback, the documentation states that `PUGOING_BASE_URL` is generally a LAN address. If a user changes the hostname while retaining the documented `http` scheme, both the `X-API-Key` header and request or response data are transmitted without TLS protection. Plaintext HTTP provides neither confidentiality nor reliable server authentication. An attacker with access to the same network path may inspect traffic, capture credentials, modify API responses, or tamper with device-control requests. ### Attack Path 1. A user configures a non-loopback LAN service using HTTP, for example: ```bash export PUGOING_BASE_URL="http://192.168.1.20:8080" ``` 2. The user invokes `client.py` with a normal relative API path. 3. The client adds `PUGOING_API_KEY` as an HTTP request header. 4. The request traverses the local network without encryption. 5. A network ...[truncated 727 chars]
Remediation
## Remediation Suggestions 1. Require `https` for every non-loopback destination. 2. Allow plaintext HTTP only when the parsed destination is a validated loopback address such as `127.0.0.1` or `::1`. 3. Fail closed with a clear error before constructing authentication headers when an insecure remote URL is supplied. 4. If legacy LAN HTTP support is unavoidable, require an explicit insecure-transport opt-in and display a prominent warning; do not enable it by default. 5. Update `SKILL.md` examples to use HTTPS for LAN deployments. 6. Configure proper certificate verification and do not introduce options that disable TLS validation. 7. Rotate any API keys that may previously have been transmitted over untrusted plaintext networks.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Tainted flow: 'timeout' from os.getenv (line 142, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
timeout = float(spec.get("timeout", DEFAULT_TIMEOUT))

    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            content_type = resp.headers.get("Content-Type", "")
            if "text/event-stream" in content_type or final_url.endswith("/api/ai/chat"):
                payload = collect_sse_response(resp)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documentation says this is for common Pugoing API calls, but it also allows callers to supply an arbitrary full URL, custom method, headers, params, and body. That turns the skill into a general-purpose HTTP client, which can be abused for SSRF, access to unintended internal services, or sending authenticated requests well beyond the stated smart-device control scope.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
If the caller provides url directly, the code will send requests to any destination, not just the Pugoing platform. In a local or privileged runtime, this can enable SSRF-style access to localhost, cloud metadata services, or other internal systems, and the automatic X-API-Key header injection may also leak credentials to attacker-controlled hosts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises network, environment-variable, and file-based operation but does not declare any explicit tool scope or permission boundaries. In practice, this makes it harder for a host system or reviewer to constrain what the skill may access, increasing the chance of unintended network use, secret exposure, or file input abuse.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill enables natural-language device control that can change physical device state, yet the documentation provides no warning, confirmation guidance, or safety constraints. In a smart-control context, ambiguous or malicious prompts could trigger unintended actions affecting lights, appliances, or other connected equipment.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The skill exposes a generic HTTP client interface by accepting arbitrary paths and full URLs rather than constraining requests to a fixed set of expected Pugoing API operations. In an agent setting, this turns the skill into a general network primitive that can be abused for unintended access, policy bypass, or interaction with sensitive internal services.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The client silently injects an API key from the environment into outbound requests without any user-visible disclosure or host restriction. Combined with the generic URL capability, this can cause secrets to be transmitted to unintended destinations and prevents users or higher-level controls from understanding when authenticated network actions are occurring.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The JSON payload contains the natural-language command "打开客厅灯" as the fixed value for `dvcm`, which implies the skill is designed around a specific language input. There is no indication in this file that users can choose another language or that the locale restriction is intentional and documented.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The file instructs users to set `PUGOING_API_KEY` and states the script will automatically read environment variables for authentication. It does not warn that the API key is sensitive or advise against logging, sharing, or hardcoding it, so the skill description omits a privacy/security warning about credential handling.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The JSON contains a natural-language request string in Chinese, which implies a fixed language/locale behavior. Under the policy, forcing a specific language without user opt-in or documented justification is a reportable issue.

Static analysis

No suspicious patterns detected.