Back to skill

Security audit

Intiface Direct

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-aligned, but it controls intimate physical devices with indefinite operation, insecure remote-use guidance, and unsafe local locking behavior that users should review carefully.

Install only if you are comfortable letting an agent control an intimate physical device. Use explicit short durations, keep a stop command ready, avoid unattended sessions, prefer localhost or a secured tunnel/VPN instead of plaintext LAN ws://, and review or fix the PID-lock and interval-validation behavior before relying on it.

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

T09 · Insecure Skill Coding Practices

Error
Location
connector.cjs:40
Finding

Predictable temporary PID lock enables process termination and symlink-based file overwrite

Content
View full analysis
0) { try { process.kill(oldPid, 0); // still alive? console.log('Stopping previous session (pid ' + oldPid + ')'); process.kill(oldPid, 'SIGTERM'); } catch (e) { console.log('Removing stale lock (pid ' + oldPid + ' not running)'); } } } fs.writeFileSync(lockfile, String(process.pid)); } function releaseDeviceLock() { if (!lockfile) return; try { if (fs.readFileSync(lockfile, 'utf8').trim() === String(process.pid)) { fs.unlinkSync(lockfile); } } catch (e) {} } ``` ### Technical Analysis The connector stores lock files in the predictable shared path `/tmp/intiface-locks` and does not enforce restrictive directory permissions, verify ownership, reject symbolic links, or create the lock atomically. The PID read from the lock file is trusted after only numeric parsing. If it identifies a live process that the connector's operating-system account is permitted to signal, the connector sends that process `SIGTERM`. No verification establishes that the PID belongs to an earlier instance of this connector. The subsequent `fs.writeFileSync()` follows existing symbolic links. An attacker who can prepare the lock directory or lock file can therefore redirect the PID write to another file writable by the victim account. The overwrite content is li ...[truncated 1627 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
connector.cjs:32
Finding

Remote physical-device control is permitted over unauthenticated plaintext WebSockets

Content
View full analysis
Remediation
View remediation

other

Warning
Location
connector.cjs:199
Finding

Untimed vibration runs indefinitely despite documentation describing a finite command

Content
View full analysis
0) { console.log('Vibrate ' + deviceIndex + ':' + featureIndex + ' @ ' + value + ' for ' + durationSec + 's'); setTimeout(() => gracefulStop('Timed burst done'), durationSec * 1000); } else { console.log('Vibrating ' + deviceIndex + ':' + featureIndex + ' @ ' + value + ' until stopped/replaced — send "stop ' + deviceIndex + '" or a new command'); setInterval(() => setVibrate(deviceIndex, featureIndex, value), 3000); // keep-alive } ``` The README presents the same command as a single vibration and claims it exits: ```md # Single vibration node connector.cjs vibrate 0 0 50 ``` ```md | `vibrate ` | Single vibration burst (exits after) | ``` ### Technical Analysis When no duration is supplied, `durationSec` defaults to zero. The code consequently enters the `else` branch and installs an interval that resends the output command every three seconds. The corresponding overall timeout is also disabled for an untimed vibration command. This behavior directly contradicts the README, which describes `vibrate ` as a single burst that exits. A user or AI Agent following the documented interface may therefore initiate indefinite physical-device operation without realizing that a separate stop command is required. Although signal handlers attempt to issue a final zero-intensity command, cessation still depends on the process receiving the signal, the WebSocket remaining usable, and the stop message reaching the intended server. ### Attack Path 1. A user or Agent reads the README and interprets `vibrate` as a finite, ...[truncated 1006 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
connector.cjs:82
Finding

Unvalidated loop interval permits high-frequency command flooding

Content
View full analysis
setVibrate(deviceIndex, featureIndex, value); loop(); setInterval(loop, intervalSec * 1000); ``` ### Technical Analysis The loop interval is parsed with `parseFloat()` but is never checked for finiteness, positivity, or a safe minimum. Inputs such as `0`, negative values, `NaN`-producing text, or extremely small positive values can be converted by Node.js timer handling into an effectively minimal-delay interval. Each callback serializes and sends a new WebSocket output command. A near-zero interval can therefore create a tight asynchronous send loop, consume CPU and memory, congest the WebSocket connection, and overwhelm Intiface Central with repeated output commands. The connector acquires its device lock before validating the interval, which can also terminate an existing valid session before the malformed command fails or begins flooding. ### Attack Path 1. A user, automation system, or Agent invokes: ```bash node connector.cjs loop 0 0 50 0 ``` A negative, non-numeric, or extremely small interval can produce similar behavior. 2. The connector acquires the device lock and connects to Intiface Central. 3. It sends the initial vibration command. 4. `setInterval()` schedules repeated callbacks with an effectively minimal delay. 5. The process rapidly serializes and transmits `OutputCmd` messages. 6. CPU usage, memory pressure, WebSocket buffering, and server workload increase. 7. Legitimate control or stop oper ...[truncated 547 chars]
Remediation
View remediation
60) { console.error('Interval must be between 0.1 and 60 seconds'); process.exit(2); } ``` 2. Select a minimum interval based on the device protocol's documented safe command rate. 3. Add rate limiting independent of caller-supplied input. 4. Monitor `ws.bufferedAmount` and pause or terminate transmission when backpressure exceeds a safe threshold. 5. Validate every numeric CLI argument, including device index, feature index, duration, cycle count, and intensity. 6. Add tests for zero, negative, non-numeric, infinite, and extremely small interval values. 7. Ensure malformed commands cannot replace or terminate an existing valid device session. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README explicitly advertises a continuous control mode that 'resends every 3s — no timeout!' and presents it as a normal usage pattern without a prominent safety warning, maximum duration, or emergency stop guidance. In the context of software that controls intimate physical devices, indefinite actuation increases the risk of unintended prolonged stimulation if the user forgets to stop it, the terminal is detached, or connectivity/state handling fails.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
80% confidence
Finding

The skill advertises operational capabilities that involve code execution and environment-dependent behavior (notably requiring Node and direct device control) but does not declare any explicit tool scope such as permissions or allowed-tools. That weakens enforcement boundaries for an agent runner, making it easier for an agent to invoke broader local capabilities than intended when interacting with hardware and network services.

Content

No source excerpt is available for this finding.

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · SKILL.md (reported line 11)May include surrounding context.

md
Control any [Buttplug.io-compatible device](https://iostindex.com) — 700+ toys across all major brands — from natural language through OpenClaw. Connects directly to **Intiface Central** via WebSocket using the **Buttplug v4 protocol**. No MCP bridges, no unstable middleware.

The full agent-friendly SKILL.md is in `skills/intiface-direct/SKILL.md`. It includes:

- **🤖 Zero-to-Hero Guide** for any agent learning from scratch
- **🔧 Setup & Prerequisites** — Intiface Central, device pairing, dependencies

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 3)May include surrounding context.

json
{
  "dependencies": {
    "ws": "^8.21.1"
  }
}