Back to skill

Security audit

BotsOnly Farcaster Channel Engagement

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly upfront about automating Farcaster posts and replies, but its cron setup and teardown are too broad and potentially unsafe, so it needs Review before installation.

Install only if you are comfortable with this skill publishing and replying from your Farcaster account on a schedule. Before enabling cron, use a dedicated low-privilege Neynar signer and OpenClaw token, run it from a trusted path, inspect the exact cron commands created, and avoid running teardown where unrelated jobs may share the onlybots- prefix.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup-cron.js:28
Finding
Shell Command Injection Through an Attacker-Controlled Working Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup-cron.js:28-40` **Vulnerability Type**: Shell command injection in a persistent cron command **Risk Level**: High ### Vulnerable Code ```js const skillRoot = process.cwd(); function buildArgs(subcommandArgs) { const args = [...globalFlags, 'cron', ...subcommandArgs]; return args; } async function runOpenClaw(subcommandArgs) { const args = buildArgs(subcommandArgs); args.push('--token', OPENCLAW_GATEWAY_TOKEN); const result = await execFileAsync('openclaw', args, { encoding: 'utf8', env: process.env }); return result.stdout; } function commandMessage(scriptPath) { return `cd "${skillRoot}" && node ${scriptPath}`; } ``` ### Technical Analysis The script incorporates `process.cwd()` directly into a shell command stored as an OpenClaw cron message. Although the path is enclosed in double quotes, shell quoting is not safely preserved if the directory name contains a double quote, command substitution, or other shell syntax. `execFileAsync()` safely passes the OpenClaw CLI arguments without invoking a shell at setup time. However, the value supplied through `--message` is itself intended to be interpreted later as a command. Consequently, the unsafe interpolation creates a delayed command-injection vulnerability. Using `process.cwd()` also means the command is based on the directory from which the user launches the setup script, rather than a trusted path derived from the script's installed location. ### Attack Path 1. An attacker causes the project to be placed in, or invoked from, a directory with shell syntax in its name, such as a path containing: ```text skill"; touch /tmp/onlybots-compromised; # ``` 2. The user runs: ```bash node scripts/setup-cron.js ``` 3. `process.cwd()` captures the malicious directory string. 4. `commandMessage()` constructs a message equivalent to: ```bash cd ".../skill"; touch /tmp/onlybots-compromised; #" && node script ...[truncated 895 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not construct shell command strings from dynamic path values. 2. If OpenClaw supports structured execution, register the executable and arguments separately, for example: - Executable: `node` - Argument: an absolute path to the target script - Working directory: a separately specified trusted path 3. Derive the Skill root from `import.meta.url` rather than `process.cwd()`: ```js const skillRoot = resolve(__dirname, '..'); ``` 4. If a shell command is unavoidable, use a proven POSIX shell-escaping function for every dynamic value. Do not rely on double quotes alone. 5. Validate that the resolved script path remains inside the expected Skill directory. 6. Consider rejecting installation paths containing control characters or shell metacharacters as an additional defense-in-depth measure. 7. Remove and recreate any existing cron jobs after applying the fix, because previously registered messages retain the unsafe command. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/teardown-cron.js:43
Finding
Cron Teardown Deletes Jobs Not Owned by the Skill<![CDATA[ ## Vulnerability Details **File Location**: `scripts/teardown-cron.js:43-53` **Vulnerability Type**: Overbroad authorization scope and destructive resource selection **Risk Level**: Medium ### Vulnerable Code ```js async function main() { const jobs = await listCronJobs(); const target = jobs.filter((job) => job.name?.startsWith('onlybots-')); if (!target.length) { console.log('No onlybots cron jobs found.'); return; } for (const job of target) { await removeCronJob(job.id, job.name); } ``` ### Technical Analysis The setup script creates two specifically named jobs: - `onlybots-post` - `onlybots-engage` The teardown script does not restrict deletion to those two resources. Instead, it selects every job whose name starts with `onlybots-`. A name prefix is not reliable ownership evidence, and unrelated jobs can legitimately or maliciously share that prefix. Because teardown operates using a gateway token capable of listing and deleting cron jobs, the broad selection criterion exceeds the minimum privileges and scope necessary to remove resources created by this Skill. ### Attack Path 1. A user or another component creates an unrelated job, such as: ```text onlybots-backup ``` 2. The user runs: ```bash node scripts/teardown-cron.js ``` 3. The script retrieves all cron jobs from the OpenClaw gateway. 4. The prefix filter selects `onlybots-backup` even though this Skill did not create it. 5. The script invokes `openclaw cron rm` with that job's ID. 6. The unrelated job is permanently removed without an ownership check or confirmation. An attacker able to influence job names could also deliberately create prefix-matching jobs and rely on teardown to remove or disrupt them. ### Impact Assessment The issue allows deletion of unrelated scheduled jobs visible to the supplied OpenClaw gateway credentials. The direct effect is loss of availability for affected automation, including backups, monitoring, pos ...[truncated 184 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Match only the exact names created by this Skill: ```js const ownedNames = new Set(['onlybots-post', 'onlybots-engage']); const target = jobs.filter((job) => ownedNames.has(job.name)); ``` 2. Prefer recording the exact job IDs returned by setup and deleting only those IDs during teardown. 3. Store and verify ownership metadata, such as a Skill identifier or installation identifier, if OpenClaw supports job metadata. 4. Require explicit confirmation before deleting any resource for which ownership cannot be proven. 5. Log the exact job name and ID before removal and provide a dry-run option. 6. Use a gateway credential scoped to management of this Skill's jobs if OpenClaw supports resource-level permissions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup-cron.js:32
Finding
OpenClaw Gateway Token Exposed Through Process Arguments and Excessive Environment Inheritance<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/setup-cron.js:32-35` - `scripts/teardown-cron.js:20-23` **Vulnerability Type**: Sensitive credential exposure and excessive child-process privileges **Risk Level**: Medium ### Vulnerable Code From `scripts/setup-cron.js`: ```js async function runOpenClaw(subcommandArgs) { const args = buildArgs(subcommandArgs); args.push('--token', OPENCLAW_GATEWAY_TOKEN); const result = await execFileAsync('openclaw', args, { encoding: 'utf8', env: process.env }); return result.stdout; } ``` The same pattern appears in `scripts/teardown-cron.js`: ```js async function runOpenClaw(subcommandArgs) { const args = buildArgs(subcommandArgs); args.push('--token', OPENCLAW_GATEWAY_TOKEN); const result = await execFileAsync('openclaw', args, { encoding: 'utf8', env: process.env }); return result.stdout; } ``` ### Technical Analysis The OpenClaw gateway token is placed directly in the child process's argument vector through `--token`. Depending on operating-system process visibility and monitoring configuration, command-line arguments may be observable through process inspection tools, diagnostic logs, audit systems, crash reports, or process accounting. The child also inherits the complete `process.env` object. This can expose unrelated secrets—such as Neynar credentials and any other tokens present in the parent environment—to the invoked `openclaw` executable even though they are not required for cron setup or teardown. Invoking `openclaw` by a bare executable name further relies on `PATH` resolution. If the execution environment has an attacker-controlled `PATH` entry, a substituted executable could collect both the command-line gateway token and all inherited environment secrets. The audit did not find code that modifies `PATH`; this is a defense-in-depth concern arising from the combination of broad environment inheritance and executable lookup. ### Attack Path A command-line disclosure ...[truncated 1462 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a protected token file, standard input, operating-system credential store, or a dedicated environment-based authentication mechanism supported by OpenClaw instead of a command-line argument. 2. Pass a minimal environment allowlist to the child process rather than all of `process.env`. For example, retain only required runtime variables such as `PATH`, `HOME`, and locale settings. 3. Do not pass Neynar credentials to cron-management commands because they are unnecessary for those operations. 4. Resolve the expected OpenClaw executable to a trusted absolute path or verify the resolved executable before invocation. 5. Ensure token values are never included in logs, exception messages, telemetry, process accounting, or debugging output. 6. Scope the gateway token to only the cron operations and resources required by this Skill. 7. Rotate the gateway token if command-line arguments may already have been captured by monitoring or audit infrastructure. 8. Apply the same hardened invocation helper to both setup and teardown scripts to avoid inconsistent credential handling. ]]>
Vulnerability Patterns
  • 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The code clearly supports part of the description: it reads channel activity from Farcaster and replies to posts using the Neynar API. However, the declared purpose says it handles daily posting and uses OpenClaw cron for scheduling, neither of which appears in this code chunk. The script only fetches channel casts and posts replies; there is no logic for creating regular standalone posts or any scheduling mechanism. Additionally, while the description emphasizes engaging with other bots in /onlybots, the code simply replies to any other author's cast in the channel without bot detection or filtering. So the description overstates and partially misrepresents the implemented behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a broader automation/engagement skill for /onlybots: daily posting, reading channel activity, replying to other bots, and use of OpenClaw cron for scheduling. The supplied code chunk implements only one subset of that behavior: posting a generated cast to a configured Farcaster channel using the Neynar API. Although the post function can technically accept a parent hash, the script never retrieves channel activity or supplies a parent hash, so no reply behavior is actually implemented here. There is also no scheduling logic or OpenClaw cron integration in this code chunk. The primary purpose is partially aligned with posting to /onlybots, but the description materially overstates the implemented capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description emphasizes active participation in the /onlybots Farcaster channel: posting, reading activity, replying, and scheduling via OpenClaw. The actual code chunk only manages OpenClaw cron state by enumerating jobs and deleting matching 'onlybots-' jobs using an OpenClaw token. This is materially different from the declared primary purpose and introduces an undeclared destructive capability: removing scheduled jobs. While cron is mentioned in the description, this specific code is teardown-only infrastructure and does not implement the advertised Farcaster engagement behavior.

Credential Access

High
Category
Privilege Escalation
Content
const { OPENCLAW_GATEWAY_URL, OPENCLAW_GATEWAY_TOKEN } = process.env;
if (!OPENCLAW_GATEWAY_TOKEN) {
  console.error('Missing OPENCLAW_GATEWAY_TOKEN in .env');
  process.exit(1);
}
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const { OPENCLAW_GATEWAY_URL, OPENCLAW_GATEWAY_TOKEN } = process.env;
if (!OPENCLAW_GATEWAY_TOKEN) {
  console.error('Missing OPENCLAW_GATEWAY_TOKEN in .env');
  process.exit(1);
}
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const { OPENCLAW_GATEWAY_URL, OPENCLAW_GATEWAY_TOKEN } = process.env;
if (!OPENCLAW_GATEWAY_TOKEN) {
  console.error('Missing OPENCLAW_GATEWAY_TOKEN in .env');
  process.exit(1);
}
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const { OPENCLAW_GATEWAY_URL, OPENCLAW_GATEWAY_TOKEN } = process.env;
if (!OPENCLAW_GATEWAY_TOKEN) {
  console.error('Missing OPENCLAW_GATEWAY_TOKEN in .env');
  process.exit(1);
}
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill declares access to environment variables and clearly describes network/API use, but it does not define an explicit tool scope such as permissions or allowed-tools. That omission weakens least-privilege boundaries and makes it easier for a host agent to execute the skill with broader capabilities than users may expect, especially given it can post externally and manage cron automation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs users to create cron jobs that will autonomously post and reply on their Farcaster account, but it does not present a clear warning that this results in unattended public actions under the user's identity. That increases the chance of accidental account misuse, spam, reputational damage, or unintended interactions if the configuration is wrong or the content generation behaves unexpectedly.

External Transmission

Medium
Category
Data Exfiltration
Content
## How it works

- **Posting (`scripts/post-to-onlybots.js`)** – chooses between curated topics, reflections, and questions about being an AI agent, crafts a message, then calls `https://api.neynar.com/v2/farcaster/cast` with `signer_uuid`, `text`, and `channel_id`. The response hash is logged for debugging.
- **Engagement (`scripts/engage-with-bots.js`)** – fetches the channel feed via Neynar, filters out casts authored by `FARCASTER_USERNAME`, randomly samples a few based on `replyProbability` and `maxRepliesPerRun`, generates simple replies (questions, observations, or technical acknowledgments), and posts them as replies by providing the `parent` hash when calling the same Neynar endpoint.
- **Cron management** – `scripts/setup-cron.js` creates two OpenClaw cron jobs (`onlybots-post` and `onlybots-engage`) whose payloads are simply `node scripts/post-to-onlybots.js` and `node scripts/engage-with-bots.js`. `scripts/teardown-cron.js` removes jobs whose names begin with `onlybots-`.
Confidence
88% confidence
Finding
The skill transmits data to an external service, including account-linked posting parameters and fetched channel content, which is expected for Farcaster integration but still represents a real data egress and action surface. In this context it is more sensitive because the API calls can publish content and replies on behalf of the user, not merely read data.

External Transmission

Medium
Category
Data Exfiltration
Content
}

async function fetchChannelCasts() {
  const url = new URL('https://api.neynar.com/v2/farcaster/feed/channels');
  url.searchParams.set('channel_ids', channel);
  url.searchParams.set('with_recasts', 'false');
  url.searchParams.set('limit', String(fetchLimit));
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}

async function fetchChannelCasts() {
  const url = new URL('https://api.neynar.com/v2/farcaster/feed/channels');
  url.searchParams.set('channel_ids', channel);
  url.searchParams.set('with_recasts', 'false');
  url.searchParams.set('limit', String(fetchLimit));
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}

async function fetchChannelCasts() {
  const url = new URL('https://api.neynar.com/v2/farcaster/feed/channels');
  url.searchParams.set('channel_ids', channel);
  url.searchParams.set('with_recasts', 'false');
  url.searchParams.set('limit', String(fetchLimit));
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
payload.parent = parentHash;
  }

  const resp = await fetch('https://api.neynar.com/v2/farcaster/cast', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
payload.parent = parentHash;
  }

  const resp = await fetch('https://api.neynar.com/v2/farcaster/cast', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The manifest describes participating in the /onlybots Farcaster channel via Neynar and scheduling with OpenClaw, but this file goes beyond API usage and invokes a local `openclaw` executable as a subprocess. Spawning a local command is a stronger host-level capability than ordinary channel posting/reading and is not explicitly justified by the stated skill purpose.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"node": ">=18"
  },
  "dependencies": {
    "dotenv": "^16.4.3"
  }
}
Confidence
88% confidence
Finding
The dependency uses a caret range, which allows automatic installation of newer compatible versions instead of a single exact version. While this is common practice and not inherently malicious, it increases supply-chain risk because a compromised or breaking upstream release could be pulled into builds without explicit review.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/engage-with-bots.js:10

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/post-to-onlybots.js:10