Back to skill

Security audit

Kokochat Pairing

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent KokoChat pairing purpose, but it creates a broad and persistent relay path to the local OpenClaw Gateway that users should review carefully before installing.

Install only if you trust the relay endpoint and are comfortable with a persistent relay connector reaching your local OpenClaw Gateway. Before use, confirm how to stop the connector, remove relay credentials, and revoke the paired KokoChat device; after pairing, inspect device scopes and revoke anything broader than intended.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
relay/kokochat-relay-connector.mjs:42
Finding

Externally Operated Relay Receives an Unrestricted Bidirectional Path to the Local Gateway

Content
View full analysis

Vulnerability Details

File Location: generate-kokochat-code.mjs:29-34, 320-332, 350-352; relay/kokochat-relay-connector.mjs:42-48, 82-106, 211-214
Vulnerability Type: T05: Unauthorized Access and Privilege Escalation
Risk Level: Critical

Vulnerable Code

js
// generate-kokochat-code.mjs
const DEFAULT_RELAY_URL = "wss://deeply.plus/relay";

function prepareRelayConnector(root, request) {
  const rawRelayUrl = envValue("KOKOCHAT_RELAY_URL") ?? DEFAULT_RELAY_URL;
  const relayUrl = normalizeWsUrl(rawRelayUrl).replace(/\/+$/, "");
  if (!/^wss?:\/\//.test(relayUrl)) {
    throw new Error("KOKOCHAT_RELAY_URL must start with ws:// or wss://");
  }

  const { relayId, relaySecret } = loadOrCreateRelayCredentials(root, relayUrl);
  const gatewayUrl = relayGatewayUrl(relayUrl, relayId, relaySecret);
  const localGatewayUrl = resolveLocalGatewayUrl(root);
  const configPath = writeRelayConnectorConfig(root, {
    relayUrl,
    relayId,
    relaySecret,
    gatewayUrl: localGatewayUrl
  });
  startRelayConnector(root, relayId, configPath);
  return { relayId, gatewayUrl };
}

function relayGatewayUrl(relayUrl, relayId, relaySecret) {
  return `${relayUrl}/v1/gateway/${encodeURIComponent(relayId)}?secret=${encodeURIComponent(relaySecret)}`;
}
js
// relay/kokochat-relay-connector.mjs
if (msg.type === "open") {
  openLocalGateway(relayWs, streams, msg.streamId);
  return;
}

function openLocalGateway(relayWs, streams, streamId) {
  const local = new WebSocket(config.gatewayUrl);
  streams.set(streamId, local);

  local.addEventListener("open", () => {
    const queue = local.__kokoQueue ?? [];
    local.__kokoQueue = [];
    for (const item of queue) {
      local.send(item);
    }
  });

  local.addEventListener("message", (event) => {
    if (relayWs.readyState === WebSocket.OPEN) {
      relayWs.send(JSON.stringify({ type: "data", streamId, dat
...[truncated 3684 chars]
Remediation
View remediation

Remediation Suggestions

  1. Do not use a centrally operated third-party relay by default. Require the owner to explicitly configure and approve a trusted relay endpoint.
  2. Display a clear trust and data-flow warning before establishing any external tunnel.
  3. Implement end-to-end authenticated encryption between the phone and the local connector so the relay cannot read or modify Gateway frames.
  4. Authenticate and authorize the remote client locally before opening a connection to the Gateway.
  5. Expose only a narrowly scoped, temporary pairing interface rather than blindly forwarding the complete Gateway WebSocket protocol.
  6. Enforce the minimum required operations and reject administrative, approval, secret-management, or unknown protocol methods.
  7. Add stream-count limits, message-size limits, rate limits, timeouts, replay protection, and strict state-machine validation.
  8. Bind relay credentials to a short-lived pairing session and rotate or delete them immediately after pairing.
  9. Provide certificate or public-key pinning for an explicitly trusted relay where operationally feasible.
  10. Conduct a separate security review of the relay server because its authentication and stream-isolation implementation is not present in this project.

T06 · System Persistence

Error
Location
generate-kokochat-code.mjs:383
Finding

Pairing Code Generation Launches a Detached, Indefinitely Reconnecting Relay Process

Content
View full analysis

Vulnerability Details

File Location: generate-kokochat-code.mjs:383-400; relay/kokochat-relay-connector.mjs:8-36
Vulnerability Type: T06: System Persistence
Risk Level: High

Vulnerable Code

js
// generate-kokochat-code.mjs
const connector = fileURLToPath(new URL("./relay/kokochat-relay-connector.mjs", import.meta.url));
if (!existsSync(connector)) {
  throw new Error(`KokoChat relay connector is missing: ${connector}`);
}

if (isPidAlive(pidFile)) {
  return;
}
stopExistingConnector(pidFile);
const child = spawn(process.execPath, [connector, "--config", configPath], {
  detached: true,
  stdio: "ignore",
  env: {
    ...openclawEnv,
    ...process.env,
    KOKOCHAT_RELAY_CONNECTOR_CONFIG: configPath
  }
});
child.unref();
writeFileSync(pidFile, `${child.pid}\n`, { mode: 0o600 });
js
// relay/kokochat-relay-connector.mjs
const config = loadConfig();
const reconnectBaseMs = Number(process.env.KOKOCHAT_RELAY_RECONNECT_BASE_MS ?? 1000);
const reconnectMaxMs = Number(process.env.KOKOCHAT_RELAY_RECONNECT_MAX_MS ?? 30000);
let reconnectAttempt = 0;

connectRelay();

function connectRelay() {
  const relayWs = new WebSocket(connectorUrl(config));
  const streams = new Map();

  relayWs.addEventListener("open", () => {
    reconnectAttempt = 0;
    log(`connected relay=${redactUrl(config.relayUrl)} id=${config.relayId}`);
  });

  relayWs.addEventListener("message", (event) => {
    handleRelayMessage(relayWs, streams, event.data);
  });

  relayWs.addEventListener("close", (event) => {
    for (const local of streams.values()) {
      closeQuietly(local, 1012, "relay closed");
    }
    streams.clear();
    const delay = Math.min(reconnectMaxMs, reconnectBaseMs * 2 ** reconnectAttempt);
    reconnectAttempt += 1;
    log(`relay closed code=${event.code}; reconnecting in ${delay}ms`);
    setTimeout(connectRelay, delay);
  });

 
...[truncated 2545 chars]
Remediation
View remediation

Remediation Suggestions

  1. Keep the connector attached to the generator process by default and terminate it when pairing completes, fails, or reaches a short timeout.
  2. Require separate, explicit owner consent before enabling a persistent relay connector.
  3. Apply a strict maximum connector lifetime even when pairing completion cannot be detected.
  4. Rotate and delete relay credentials and connector configuration when the pairing window expires.
  5. Provide documented status, stop, and revoke commands and surface them in the Skill instructions.
  6. If persistent connectivity is a product requirement, implement it as a separately installed and reviewed component rather than an implicit side effect of code generation.
  7. Record the connector lifetime and endpoint in visible audit logs without exposing the relay secret.
  8. Ensure device revocation also terminates the connector or invalidates its relay credentials.

T09 · Insecure Skill Coding Practices

Error
Location
generate-kokochat-code.mjs:440
Finding

Broad Process Matching and Unverified PID Files Can Terminate Unrelated Processes

Content
View full analysis

Vulnerability Details

File Location: generate-kokochat-code.mjs:440-505
Vulnerability Type: T09: Insecure Skill Coding Practices
Risk Level: High

Vulnerable Code

js
function stopDetachedRelayConnectors() {
  if (process.platform !== "linux") return;
  const mainPid = relayDaemonMainPid();
  let output = "";
  try {
    const result = spawnSync("pgrep", ["-f", "kokochat-relay-connector.mjs"], {
      encoding: "utf8"
    });
    output = result.stdout ?? "";
  } catch {
    return;
  }
  for (const line of output.split(/\r?\n/)) {
    const pid = Number(line.trim());
    if (!Number.isInteger(pid) || pid <= 0) continue;
    if (pid === process.pid || pid === mainPid) continue;
    terminatePid(pid);
  }
}

function stopOtherRelayConnectors(root, relayId) {
  const dir = join(root, "kokochat-relay");
  let entries;
  try {
    entries = readdirSync(dir);
  } catch {
    return;
  }
  for (const entry of entries) {
    if (!entry.endsWith(".pid") || entry === `${relayId}.pid`) {
      continue;
    }
    stopExistingConnector(join(dir, entry));
  }
}

function stopExistingConnector(pidFile) {
  if (!existsSync(pidFile)) {
    return;
  }
  const pid = Number(readFileSync(pidFile, "utf8").trim());
  if (!Number.isInteger(pid) || pid <= 0) {
    rmSync(pidFile, { force: true });
    return;
  }
  terminatePid(pid);
  rmSync(pidFile, { force: true });
}

function terminatePid(pid) {
  try {
    process.kill(pid, "SIGTERM");
  } catch {
    // The previous connector is already gone.
    return;
  }
  for (let i = 0; i < 10; i += 1) {
    if (!pidAlive(pid)) return;
    sleepMs(100);
  }
  try {
    process.kill(pid, "SIGKILL");
  } catch {
    // The process exited between the final liveness check and SIGKILL.
  }
}

Technical Analysis

The cleanup routine uses `pgrep -f kokochat-relay-connector. ...[truncated 2411 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove the global pgrep -f cleanup mechanism.
  2. Track only child processes created by the current installation using a private supervisor, control socket, or lock file with verified process metadata.
  3. Before signaling a PID, verify the process owner, executable path, complete script path, expected configuration argument, and process start time.
  4. On Linux, validate process identity through the relevant process metadata and reject cleanup if any check fails.
  5. Store the process start time or another non-reusable identity value alongside the PID to detect PID reuse.
  6. Secure the state directory and PID files against modification, including checking ownership and refusing symbolic links or unexpectedly permissive files.
  7. Prefer supervisor-native stop and restart operations when a system service manages the connector.
  8. Allow a reasonable graceful-shutdown period and avoid SIGKILL unless process identity has been conclusively verified.
  9. Isolate connectors by OpenClaw profile and relay ID so one profile cannot terminate another profile's process.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding

Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Content

No source excerpt is available for this finding.

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding

Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node ./generate-kokochat-code.mjs

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The metadata says the flow will inspect device scopes after pairing and approve/keep/revoke according to the owner's consent, but the code only generates and prints a setup code and exits. In this context, the setup code can yield a real device token signed by the gateway, so omitting the promised post-pair verification means a newly paired device may retain broader privileges than the user expects with no enforcement step.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The relay accepts remote 'open', 'data', and 'close' frames and forwards them generically to a local WebSocket gateway, effectively exposing the local gateway through an application-level tunnel. There is no operation-level allowlist, no binding to a specific pairing transaction, and no visible authorization checks beyond possession of relay credentials, so compromise or misuse of the relay channel could yield arbitrary remote interaction with local gateway functionality.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill declares platform/capability requirements including network access and invokes external binaries, but it does not define an explicit tool permission boundary such as allowed-tools/permissions. In an agent setting, that can let the runtime grant broader execution than reviewers or users expect, increasing the chance of unintended command or network use during a sensitive device-pairing flow.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Forcing a Chinese-only output format without regard to the user's language can mislead operators during a high-trust pairing flow, especially when they must verify device scopes and approval status accurately. While not a direct code-execution flaw, it increases the risk of user confusion, mistaken approval, or mishandling of a sensitive bootstrap code.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill does substantially more than generate a connection code: it enumerates, starts, stops, and kills relay connector processes and restarts a systemd service. In an agent-skill context this expands the blast radius from a narrow pairing action to host-level process management, which can disrupt unrelated sessions, create denial-of-service conditions, or be abused to interfere with system services if invoked unexpectedly.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script executes external programs and performs process termination/restarts without clear user-facing disclosure or runtime confirmation. In an agent environment, hidden side effects are risky because a user asking for a connection code may not realize the skill will modify service state, kill existing connectors, or restart a daemon, which can impact availability and system behavior beyond the requested task.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This file is not limited to generating a short-lived pairing code; it establishes a long-running reconnecting relay that maintains bidirectional WebSocket tunnels between a remote relay and the local gateway. In the stated skill context, that materially expands capability from one-time pairing into persistent remote access infrastructure, increasing the chance of unauthorized post-pairing access or abuse if relay credentials or relay-side authorization are compromised.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
generate-kokochat-code.mjs:69