Back to skill

Security audit

Pilot Presence

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent presence-tracking helper, but its example loop uses an unsafe predictable temp file pattern that users should fix before running continuously.

Install only if you already use Pilot Protocol and understand that this skill can run Bash commands that publish agent presence data. Before using the example loop, replace the fixed /tmp status file with a private temporary directory or another authenticated status source, validate status values, and avoid running it with elevated privileges.

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
SKILL.md:63
Finding

Predictable Temporary Status File Permits Symlink-Based File Overwrite and Disclosure

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 63–79
Vulnerability Type: Predictable temporary file, symlink following, and time-of-check/time-of-use exposure
Risk Level: Medium

Vulnerable Code:

bash
STATUS_FILE="/tmp/pilot-presence-status.txt"
echo "online" > "$STATUS_FILE"

while true; do
  status=$(cat "$STATUS_FILE" 2>/dev/null || echo "online")
  timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)

  info=$(pilotctl --json info 2>/dev/null)
  hostname=$(echo "$info" | jq -r '.data.hostname // "unknown"')

  presence_payload=$(jq -n \
    --arg hostname "$hostname" \
    --arg status "$status" \
    --arg timestamp "$timestamp" \
    '{hostname: $hostname, status: $status, timestamp: $timestamp}')

  pilotctl --json publish "$COORDINATOR" "presence.status" --data "$presence_payload"

Technical Analysis

The workflow stores its status in the fixed, globally predictable path /tmp/pilot-presence-status.txt. It neither creates the file atomically with exclusive permissions nor verifies that the path is a regular file owned by the executing user. Standard shell redirection and cat follow symbolic links.

A local attacker can therefore pre-create the path as a symbolic link or replace it while the infinite loop is running. During initialization, echo "online" > "$STATUS_FILE" follows the link and truncates or overwrites the target if the victim account can write to it. During later reads, a substituted link can point to another file readable by the victim. Its contents are assigned to status, safely encoded as JSON by jq, and then published to the configured coordinator. Safe JSON encoding prevents command injection but does not prevent information disclosure.

The repeatedly executed loop also creates an ongoing time-of-check/time-of-use opportunity. No ownership, type, link-count, or permission checks are performed before reads.

Attack Path

  1. A local attac ...[truncated 1689 chars]
Remediation
View remediation

Remediation Suggestions

  • Avoid a shared, predictable file entirely when status can be maintained in process memory or obtained through authenticated IPC.
  • If a file is required, create a private temporary directory atomically with mktemp -d, set a restrictive umask, and place the status file inside that directory.
  • Register a trap to remove the private directory on normal exit and common termination signals.
  • Before every read, verify that the path is a regular file, is not a symbolic link, and is owned by the expected user.
  • Use mechanisms that reject symbolic links, such as opening the file with O_NOFOLLOW in a small trusted helper, because separate shell checks remain vulnerable to races.
  • Restrict file permissions to the owner, validate status against the allowed set (online, busy, offline, and away), and reject unexpected or oversized content before publication.
  • Avoid running this presence loop with elevated privileges.

A safer shell-level starting point is:

bash
umask 077
STATUS_DIR=$(mktemp -d "${TMPDIR:-/tmp}/pilot-presence.XXXXXX") || exit 1
trap 'rm -rf -- "$STATUS_DIR"' EXIT HUP INT TERM
STATUS_FILE="$STATUS_DIR/status"
printf '%s\n' "online" > "$STATUS_FILE"

This removes the globally predictable pathname. For robust race resistance, status-file reads should still be implemented using an atomic, no-follow file open rather than separate path checks.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep

Static analysis

No suspicious patterns detected.