Back to skill

Security audit

peaq Robotics

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its ROS2 runtime purpose, but it can start long-running local ROS processes and change access-control roles without strong guardrails.

Install only if you want an agent to operate an existing peaq ROS2 workspace and you are comfortable with it starting/stopping local ROS nodes and making DID, storage, and access-control service calls. Treat role grants as sensitive actions and review commands before execution; use a private PID/log directory and stop any background nodes when finished.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lib/nodes.sh:25
Finding

Untrusted PID File Can Terminate an Unrelated Same-User Process

Content
View full analysis

Vulnerability Details

File Location: scripts/lib/nodes.sh, lines 25–42
Vulnerability Type: Untrusted PID-file handling and insufficient process identity validation
Risk Level: Medium

Vulnerable Code

bash
stop_bg() {
  local name="$1"
  local pid_file="$PID_DIR/$name.pid"
  if [[ ! -f "$pid_file" ]]; then
    echo "$name not running (no pid file)"
    return 0
  fi
  local pid
  pid="$(cat "$pid_file" 2>/dev/null || true)"
  if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
    kill "$pid"
    echo "Stopped $name (pid $pid)"
  else
    echo "$name not running (stale pid)"
  fi
  rm -f "$pid_file"
}

Related PID-directory configuration in scripts/peaq_ros2.sh, lines 79–80:

bash
LOG_DIR="${PEAQ_ROS2_LOG_DIR:-$HOME/.peaq_ros2/logs${ROS_DOMAIN_SUFFIX}}"
PID_DIR="${PEAQ_ROS2_PID_DIR:-$HOME/.peaq_ros2/pids${ROS_DOMAIN_SUFFIX}}"

The same trust issue affects the existing-process check in scripts/lib/nodes.sh, lines 7–18:

bash
local pid_file="$PID_DIR/$name.pid"
if [[ -f "$pid_file" ]]; then
  local existing_pid
  existing_pid="$(cat "$pid_file" 2>/dev/null || true)"
  if [[ -n "$existing_pid" ]] && kill -0 "$existing_pid" 2>/dev/null; then
    if ps -o stat= -p "$existing_pid" 2>/dev/null | grep -q "Z"; then
      rm -f "$pid_file"
    else
      echo "$name already running (pid $existing_pid)"
      return 0
    fi
  fi
fi

Technical Analysis

The process-management implementation treats the contents of a predictable PID file as authoritative. Before sending SIGTERM, stop_bg only checks that the value is nonempty and refers to a process visible to kill -0. It does not verify:

  • That the file contains a strictly valid positive numeric PID.
  • That the PID belongs to the ROS node represented by the filename.
  • That the process start time matches the process originally launched by the Skill.
  • That the PID fi ...[truncated 2461 chars]
Remediation
View remediation

Remediation Suggestions

  1. Validate PID syntax before using it:

    bash
    [[ "$pid" =~ ^[1-9][0-9]*$ ]] || fatal "Invalid PID file contents"
    
  2. Verify process identity before signaling it. Compare /proc/$pid/cmdline, /proc/$pid/exe, and the process start time against metadata recorded when the node was launched. Do not rely only on a command-name substring because it can be spoofed.

  3. Store more than a PID. Record the PID, process start time, expected executable, and expected arguments in a protected state file, then require all fields to match before stopping the process.

  4. Protect the state directory and files explicitly:

    bash
    umask 077
    install -d -m 700 -- "$PID_DIR"
    

    Create PID files atomically with mode 0600, and reject directories or files not owned by the current user.

  5. Canonicalize and constrain PEAQ_ROS2_PID_DIR to an approved private root. Reject symbolic links and unsafe parent-directory ownership or permissions.

  6. Validate node-name overrides before incorporating them into filenames. Use a narrow allowlist such as alphanumeric characters, underscores, and hyphens, and reject path separators.

  7. Use an exclusive lock around start and stop operations to prevent PID-file races.

  8. Prefer a process supervisor or ROS lifecycle mechanism that tracks process identity securely instead of using unauthenticated PID files.

  9. If identity verification fails, report the stale or mismatched state and remove the PID file only after safe ownership and path checks; never signal the process.

Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared purpose presents a narrowly scoped core ROS2 runtime, but the documented behavior appears to include additional capabilities beyond that summary, including a fund-request helper and possibly management of extra nodes/services. This scope drift is dangerous because users, registries, and policy systems may approve the skill under a narrower trust model than its actual behavior warrants.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared purpose presents a narrowly scoped core ROS2 runtime, but the documented behavior appears to include additional capabilities beyond that summary, including a fund-request helper and possibly management of extra nodes/services. This scope drift is dangerous because users, registries, and policy systems may approve the skill under a narrower trust model than its actual behavior warrants.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises shell, file-read, and likely network-capable behavior but does not declare any explicit tool scope such as permissions or allowed-tools. In an agent runtime, this increases the chance that the skill can invoke broader capabilities than reviewers or policy engines expect, weakening least-privilege controls.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/lib/nodes.sh (reported line 21)May include surrounding context.

sh
fi
  fi

  nohup "$@" >"$LOG_DIR/$name.log" 2>&1 &
  local pid=$!
  echo "$pid" > "$pid_file"
  echo "Started $name (pid $pid). Log: $LOG_DIR/$name.log"

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This function introduces wallet funding request behavior in a skill whose declared scope is limited to running ROS 2 nodes and calling DID, storage, and access-control services, and explicitly says it should not be used for sending funds. Even though it only prints a funding request line rather than directly transferring assets, it exposes financial-operation functionality that exceeds the manifest boundary and could be invoked by an agent or downstream tooling to initiate unauthorized funding workflows.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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.

Content

Scanner excerpt · scripts/peaq_ros2.sh (reported line 41)May include surrounding context.

sh
peaq_ros2.sh did-create [metadata_json|@json_file]
  peaq_ros2.sh did-read
  peaq_ros2.sh identity-card-json [name] [roles_csv] [endpoints_json] [metadata_json]
  peaq_ros2.sh identity-card-did-create [name] [roles_csv] [endpoints_json] [metadata_json]
  peaq_ros2.sh identity-card-did-read

  peaq_ros2.sh fund-request [amount] [reason]

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest description says this skill should be used for running an existing peaq ROS2 workspace and explicitly says not for "sending funds." The script's public interface nevertheless includes a fund-request command, which introduces finance-related behavior outside the stated operational ROS2 runtime scope, even if described as informational.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script provides a direct access-grant-role operation that can grant roles to users without any interactive confirmation, dry-run, or additional guardrail. In an agent-executed skill, this raises the chance of accidental or unauthorized privilege assignment if the command is invoked from ambiguous, mistaken, or prompt-influenced input.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.