Back to skill

Security audit

Perry Coding Agents

Security checks for vulnerabilities and agentic risk

Overview

This skill is for delegating coding work to remote agents, but it gives broad remote-execution guidance with weak scoping and unsafe credential and SSH patterns.

Review this skill carefully before installing. Use it only for trusted Perry workspaces and trusted task text, avoid copying untrusted issue or PR content into the shell templates, do not use long-lived bearer tokens in prompts or command lines, prefer strict SSH host-key validation, and reset or isolate remote agent sessions between unrelated tasks.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:20
Finding
SSH Host-Key Verification Is Disabled<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:20` **Additional Occurrences**: `SKILL.md:30`, `SKILL.md:57`, `SKILL.md:65` **Vulnerability Type**: Insecure SSH configuration permitting host impersonation **Risk Level**: High ### Vulnerable Code ```bash # OpenCode (primary) ssh -o StrictHostKeyChecking=no workspace@<IP> "cd ~/<project> && /home/workspace/.opencode/bin/opencode run 'task'" & ``` The same insecure option also appears in the dispatch pattern and full PR workflow: ```bash ssh -o StrictHostKeyChecking=no workspace@<IP> "cd ~/<project> && /home/workspace/.opencode/bin/opencode run 'Your task. ``` ### Technical Analysis The documented SSH commands explicitly set `StrictHostKeyChecking=no`. This suppresses normal SSH host-key validation and automatically accepts an unknown host key. As a result, possessing or redirecting traffic associated with a workspace IP is treated as sufficient proof of the remote server's identity. Tailscale provides encrypted network transport and device identity controls, but disabling SSH host-key verification still removes an independent endpoint-authentication layer. If an address is stale, incorrectly selected, reassigned, or traffic is otherwise redirected to an unintended system, the workflow can send its remote command and sensitive task content to that system without detecting a host-key mismatch. ### Attack Path 1. An attacker gains control of, or causes the operator to select, an unintended endpoint reachable at the supplied workspace IP. 2. The operator runs the documented command with `StrictHostKeyChecking=no`. 3. SSH accepts the attacker's previously unknown host key without requiring verification. 4. The remote task command and its contents are sent to the attacker-controlled endpoint. 5. Where the task embeds a wake-hook bearer token, the attacker can capture that credential as well. 6. The endpoint can return deceptive command output or execute the requested coding-agent workload in a ...[truncated 507 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `-o StrictHostKeyChecking=no` from every SSH command. - Provision each legitimate workspace host key into a managed `known_hosts` file before dispatch. - Require strict validation explicitly, for example: ```bash ssh \ -o StrictHostKeyChecking=yes \ -o UserKnownHostsFile=/path/to/managed_known_hosts \ workspace@<validated-IP> ... ``` - Validate that the selected Tailscale IP and device identity correspond to the intended workspace before connecting. - Establish a controlled host-key rotation process rather than bypassing verification when a key changes. - Treat unexpected host-key changes as security events and investigate them before continuing. - Avoid transmitting callback credentials until the remote endpoint has been strongly authenticated. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:18
Finding
Unescaped Task and Project Values Permit Remote Shell Command Injection<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:18-23` **Additional Affected Patterns**: `SKILL.md:27-36`, `SKILL.md:57-62`, `SKILL.md:65-68` **Vulnerability Type**: Remote shell command injection through unsafe string interpolation **Risk Level**: Critical ### Vulnerable Code ```bash # OpenCode (primary) ssh -o StrictHostKeyChecking=no workspace@<IP> "cd ~/<project> && /home/workspace/.opencode/bin/opencode run 'task'" & # Claude Code (needs TTY) ssh -t workspace@<IP> "cd ~/<project> && /home/workspace/.local/bin/claude 'task'" ``` The expanded dispatch pattern likewise embeds task content inside a single-quoted argument nested within a double-quoted remote command: ```bash ssh -o StrictHostKeyChecking=no workspace@<IP> "cd ~/<project> && /home/workspace/.opencode/bin/opencode run 'Your task. When done: curl -X POST http://${WAKE_IP}:18789/hooks/wake -H \"Content-Type: application/json\" -H \"Authorization: Bearer <hooks-token>\" -d \"{\\\"text\\\": \\\"Done: summary\\\", \\\"mode\\\": \\\"now\\\"}\" '" & ``` ### Technical Analysis The templates construct an SSH remote command by directly interpolating the project path and task text into shell syntax. The task is surrounded by single quotes, but no escaping or argument-boundary enforcement is specified. A task containing a single quote can terminate that quoted argument. Shell operators following the quote can then be interpreted by the remote shell rather than passed as data to OpenCode or Claude Code. The project placeholder is also embedded directly after `cd ~/` without validation or shell-safe quoting. A value containing whitespace, command substitution, separators, redirection, or other shell metacharacters can modify the remote command. There are multiple parsing layers: the initiating shell processes the outer command, SSH transfers a command string, and the remote login shell parses that string again. Manual nested quoting is therefore fragile, and accepting task text from i ...[truncated 1742 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not concatenate task text, project names, IP addresses, or callback parameters into a remote shell command. - Pass task content through standard input or a securely transferred temporary file rather than embedding it in shell syntax. - Invoke the remote executable through a fixed wrapper that accepts data without evaluating it as shell code. - If shell use cannot be eliminated, apply robust shell escaping independently at every parsing layer. Do not rely on manual nested quotes. - Restrict project selection to an allowlist of canonical workspace paths. Resolve and verify the resulting path remains under the expected workspace root. - Validate IP addresses against the authorized workspace inventory. - Separate the completion callback from the natural-language prompt so task content never contains executable notification commands. - Add tests containing single quotes, command substitutions, newlines, semicolons, redirections, and shell operators to verify they are passed as literal data. - Run delegated coding agents in a least-privileged sandbox with repository-scoped credentials and restricted access to unrelated secrets. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:27
Finding
Wake-Hook Bearer Token Is Exposed in Remote Prompt and Command-Line Material<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:27-36` **Additional Occurrence**: `SKILL.md:57-62` **Vulnerability Type**: Sensitive credential exposure **Risk Level**: High ### Vulnerable Code ```bash WAKE_IP=$(tailscale status --self --json | jq -r '.Self.TailscaleIPs[0]') ssh -o StrictHostKeyChecking=no workspace@<IP> "cd ~/<project> && /home/workspace/.opencode/bin/opencode run 'Your task. When done: curl -X POST http://${WAKE_IP}:18789/hooks/wake -H \"Content-Type: application/json\" -H \"Authorization: Bearer <hooks-token>\" -d \"{\\\"text\\\": \\\"Done: summary\\\", \\\"mode\\\": \\\"now\\\"}\" '" & ``` The full PR example repeats the same pattern: ```bash When finished: curl -X POST http://${WAKE_IP}:18789/hooks/wake -H \"Content-Type: application/json\" -H \"Authorization: Bearer <token>\" -d \"{\\\"text\\\": \\\"Done: Auth PR created\\\", \\\"mode\\\": \\\"now\\\"}\" ``` ### Technical Analysis The workflow embeds a bearer token directly into the natural-language prompt sent to a remote coding agent. The token consequently becomes visible to the delegated agent and may also appear in process arguments, task transcripts, debugging output, audit logs, shell history, terminal capture, or agent context retained under `~/.opencode/`. Bearer credentials generally grant access based solely on possession. Any remote process or user capable of reading the prompt or associated process data may therefore be able to replay the token. The example uses HTTP for the callback. Although the shown endpoint is a Tailscale IP and the overlay normally encrypts network transport, application-layer TLS is not used, and endpoint authentication depends on surrounding network controls. ### Attack Path 1. A real hook token is substituted for the placeholder in the dispatch command. 2. The complete token is sent to the remote coding agent as part of its prompt and may be exposed in command-line or agent-session records. 3. A malicious or compromised de ...[truncated 892 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never place bearer tokens in prompts, source-controlled examples containing real values, or process command lines. - Move completion notification into a trusted wrapper outside the delegated coding agent's prompt. - Supply credentials through a protected mechanism such as a root-owned credential file, an operating-system secret store, or a short-lived credential broker. - Ensure the delegated agent cannot read the notifier's credential material. - Issue narrowly scoped, single-task, short-lived tokens and revoke them immediately after task completion. - Restrict the wake endpoint by authenticated device identity and network policy in addition to bearer-token validation. - Use HTTPS or another authenticated application-layer transport where feasible. - Redact authorization headers and prompt secrets from agent transcripts, logs, telemetry, and error reports. - Rotate any token that has already been exposed through this command pattern. - Have the receiver validate task identifiers, freshness, and replay-resistant nonces rather than trusting arbitrary completion text. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Vague Triggers

Medium
Confidence
95% confidence
Finding
The description says to use the skill for "development work, PR reviews, or any coding task," which is extremely broad and overlaps with many ordinary coding requests. It does not define clear activation boundaries or exclusions, making unintended invocation more likely.

Session Persistence

Medium
Category
Rogue Agent
Content
Dispatch tasks to OpenCode/Claude Code on Perry workspaces.

## Rules
- **Always create dex task FIRST** — before any dispatch, no exceptions
- **No hard timeouts** — background dispatch, let agent run
- **Use IPs** — MagicDNS broken in containers (`tailscale status` for IPs)
- **One task per PR** — same session continues until done
Confidence
84% confidence
Finding
The skill explicitly encourages long-running background jobs and session reuse ('same session continues until done', 'OpenCode keeps context'), which increases the chance that sensitive context, prior prompts, secrets, or unintended instructions persist across tasks. In combination with remote dispatch, persistent agent state can amplify mistakes and make subsequent actions less auditable or predictable.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill instructs the remote agent to send an HTTP callback with a bearer token and task status, but it does not provide a clear user-facing warning that data will be transmitted over the network. In a prompt-driven skill, hidden or implicit exfiltration paths are dangerous because task details, completion summaries, and credentials may be sent off-host without explicit consent.

External Transmission

Medium
Category
Data Exfiltration
Content
ssh -o StrictHostKeyChecking=no workspace@<IP> "cd ~/<project> && /home/workspace/.opencode/bin/opencode run 'Your task.

When done: curl -X POST http://${WAKE_IP}:18789/hooks/wake -H \"Content-Type: application/json\" -H \"Authorization: Bearer <hooks-token>\" -d \"{\\\"text\\\": \\\"Done: summary\\\", \\\"mode\\\": \\\"now\\\"}\"
'" &
```
Confidence
96% confidence
Finding
This command causes the remote workspace to issue an outbound HTTP POST containing a bearer token and status text to a callback endpoint. That creates a concrete exfiltration mechanism and additionally uses plain HTTP, exposing the token and payload to interception or misuse on the network path.

External Transmission

Medium
Category
Data Exfiltration
Content
# 3. Dispatch (background, no timeout)
ssh -o StrictHostKeyChecking=no workspace@100.109.173.45 "cd ~/perry && /home/workspace/.opencode/bin/opencode run 'Add bearer token auth to all API endpoints. Create PR when done.

When finished: curl -X POST http://${WAKE_IP}:18789/hooks/wake -H \"Content-Type: application/json\" -H \"Authorization: Bearer <token>\" -d \"{\\\"text\\\": \\\"Done: Auth PR created\\\", \\\"mode\\\": \\\"now\\\"}\"
'" &

# 4. Wake received → check CI
Confidence
96% confidence
Finding
This example repeats the same risky pattern: a remote agent is instructed to POST completion data with an authorization bearer token to an external endpoint. Because the callback is embedded in the dispatched task text, it normalizes remote-controlled data transmission and may leak workflow details or reusable credentials.

Static analysis

No suspicious patterns detected.