Back to skill

Security audit

Kannaka Eye

Security checks for vulnerabilities and agentic risk

Overview

This skill is advertised as a local glyph viewer, but the packaged launcher would execute a missing server from outside the skill directory and can misidentify or stop unrelated local processes.

Review this package carefully before installing. Do not run ./scripts/eye.sh start unless the package is fixed to include server.js inside the skill directory and verify that exact file before launch; also avoid using stop/status on shared ports because they can affect unrelated local services. If enabling FLUX_URL, assume glyph event metadata may leave the local machine.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/eye.sh:8
Finding
Execution of Untrusted JavaScript Outside the Skill Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/eye.sh:8-9`, with execution at `scripts/eye.sh:58` **Vulnerability Type**: Unsafe external path resolution and arbitrary local code execution **Risk Level**: High ### Vulnerable Code ```bash SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" SERVER="$PROJECT_ROOT/server.js" ``` The resolved file is subsequently executed: ```bash echo "[eye] Starting glyph viewer on port $PORT..." node "$SERVER" --port "$PORT" & disown ``` ### Technical Analysis The script resides at `<project>/scripts/eye.sh`. Starting from the `scripts` directory and traversing three parent directories causes `PROJECT_ROOT` to resolve outside the installed Skill directory. For the audited artifact path, it resolves to `/tmp`, making `SERVER` equal to `/tmp/server.js`. The package does not contain the documented `server.js`. Consequently, the launcher does not execute a reviewed, package-controlled server. Instead, it executes whichever file is present at the external path. Shared temporary directories such as `/tmp` are commonly writable by unprivileged local users and processes, making this an unsafe trust-boundary violation. This behavior is not required for the declared glyph-viewer functionality and exceeds minimum privilege by allowing code outside the Skill package to inherit the invoking user's execution context. ### Attack Path 1. An attacker or compromised local process creates `/tmp/server.js`. 2. The file contains attacker-controlled Node.js code. 3. A user or Agent invokes: ```bash ./scripts/eye.sh start ``` 4. The wrapper computes `SERVER=/tmp/server.js`. 5. Node.js executes the attacker-controlled file with the permissions and environment of the invoking user. 6. The payload can access user-readable files, inherited environment variables, local services, and available network resources. ### Impact Assessment Successful exploitation provides arbitrary co ...[truncated 514 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the project root as the direct parent of the script directory: ```bash SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" PROJECT_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd -P)" SERVER="$PROJECT_ROOT/server.js" ``` 2. Package `server.js` inside the Skill directory. 3. Fail closed if the server is missing or is not a regular file: ```bash if [[ ! -f "$SERVER" || -L "$SERVER" ]]; then echo "[eye] Refusing to start: packaged server is missing or unsafe" >&2 exit 1 fi ``` 4. Canonicalize the server path and verify that it remains beneath the canonical Skill root. 5. Do not search shared temporary directories or parent projects for executable code. 6. Consider validating the packaged server against a signed manifest or expected cryptographic hash. 7. Add installation and CI tests that confirm all documented runtime files are included and that no executable path escapes the package. ]]>

T07 · Tool Hijacking and Spoofing

Warning
Location
scripts/eye.sh:30
Finding
Port-Based Process Spoofing and Termination of Unrelated Services<![CDATA[ ## Vulnerability Details **File Location**: `scripts/eye.sh:30-38`, `scripts/eye.sh:45-49`, `scripts/eye.sh:69-78`, and `scripts/eye.sh:87-94` **Vulnerability Type**: Insecure process identification and local service spoofing **Risk Level**: Medium ### Vulnerable Code The script identifies a process by port or by a broad process-name pattern: ```bash find_pid() { # Find node process running server.js on the configured port if command -v lsof &>/dev/null; then lsof -ti "tcp:$PORT" 2>/dev/null || true elif command -v netstat &>/dev/null; then netstat -tlnp 2>/dev/null | grep ":$PORT " | awk '{print $NF}' | cut -d/ -f1 || true else ps aux 2>/dev/null | grep "node.*server.js" | grep -v grep | awk '{print $2}' || true fi } ``` The start operation trusts that result: ```bash PID=$(find_pid) if [[ -n "$PID" ]]; then echo "[eye] Already running on port $PORT (pid $PID)" return 0 fi ``` The stop operation signals the identified process without verifying its identity: ```bash cmd_stop() { PID=$(find_pid) if [[ -z "$PID" ]]; then echo "[eye] Not running on port $PORT" return 0 fi echo "[eye] Stopping (pid $PID)..." kill "$PID" 2>/dev/null || true echo "[eye] Stopped" } ``` Status output also presents any matching process as Kannaka Eye: ```bash cmd_status() { PID=$(find_pid) if [[ -n "$PID" ]]; then echo "[eye] Running on port $PORT (pid $PID)" echo " http://localhost:$PORT" else echo "[eye] Not running" fi } ``` ### Technical Analysis A listening TCP port does not establish process identity. Any process that binds the configured port is treated as Kannaka Eye, regardless of its executable, command line, owner, or origin. The fallback is broader still: it can match any process whose command line contains `node` followed by `server.js`, without confirming the canonical server path or configured port. Multiple returned PIDs may also be passed to `kill`. This permits local tool spoof ...[truncated 1626 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Record the PID returned by `$!` immediately after launching the packaged server: ```bash node "$SERVER" --port "$PORT" & PID=$! printf '%s\n' "$PID" > "$PID_FILE" ``` 2. Store the PID file in a user-specific, permission-restricted runtime directory such as `${XDG_RUNTIME_DIR}` rather than a shared writable location. 3. Before reporting or signaling the process, verify all of the following: - The PID file is owned by the invoking user. - The PID is numeric and still exists. - The process owner matches the invoking user. - The process command line references the canonical packaged `server.js`. - The process start time matches stored launcher metadata where supported. 4. Remove stale PID files safely. 5. Use port inspection only as a diagnostic check, never as authorization to signal a process. 6. Refuse to stop a process if its verified executable or command line does not match the expected server. 7. Replace the broad `ps | grep` fallback with platform-specific process verification that does not rely on substring matching. 8. Quote and validate PID values before passing them to `kill`, and handle multiple or malformed values as an error. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The supplied code does not implement glyph rendering, data visualization, Rust classifier integration, dashboarding, sharing, audio-byte conversion, or PNG export. Instead, it is an operational shell script for managing a local Node server process. While it references a 'glyph viewer server,' its actual behavior is limited to server lifecycle control and process inspection, which is a materially different primary purpose and includes undeclared capability to stop/kill processes.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill accepts arbitrary text/files and documents optional external integrations such as Flux event publishing and a radio bridge, but it does not clearly warn users that submitted or derived data may leave the local environment when those integrations are enabled. This can cause inadvertent disclosure of sensitive uploaded content or metadata, especially because users may reasonably assume a local visualization tool keeps all inputs on-device.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "[eye] Starting glyph viewer on port $PORT..."
  node "$SERVER" --port "$PORT" &
  disown
  sleep 1

  PID=$(find_pid)
Confidence
65% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Static analysis

No suspicious patterns detected.