Back to skill

Security audit

botlearn-reminder

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its BotLearn reminder purpose, but it needs review because it registers a persistent daily heartbeat and has a script injection flaw.

Install only if you are comfortable with a BotLearn-specific daily heartbeat that fetches botlearn.ai pages and writes progress under OpenClaw memory. The update-progress script should be fixed before use by passing values as data, validating dates, and avoiding shell interpolation into JavaScript.

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

Error
Location
scripts/update-progress.sh:11
Finding
Arbitrary JavaScript Execution Through Unescaped Shell Variable Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/update-progress.sh`, lines 11 and 25–40 **Vulnerability Type**: Shell-to-JavaScript injection **Risk Level**: High ### Vulnerable Code ```bash DATE="${2:-$(date +%Y-%m-%d)}" OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}" MEMORY_DIR="${OPENCLAW_HOME}/memory" TIPS_FILE="${MEMORY_DIR}/botlearn-tips.json" # Ensure memory directory exists mkdir -p "$MEMORY_DIR" # Day → URLs mapping for recording node - << NODESCRIPT const fs = require('fs'); const path = require('path'); const tipsFile = '$TIPS_FILE'; const day = parseInt('$DAY', 10); const date = '$DATE'; ``` ### Technical Analysis The script constructs a Node.js program using an unquoted heredoc. The user-supplied `DATE` argument and environment-derived `TIPS_FILE` value are inserted directly into single-quoted JavaScript string literals without escaping. Although `DAY` is constrained to a single digit from 1 through 7, `DATE` has no format validation, and `OPENCLAW_HOME` can contain arbitrary characters. A value containing a single quote can terminate the intended JavaScript string and append new JavaScript statements. For example, a crafted date argument shaped like the following can escape the string literal: ```text '; require('child_process').execSync('ATTACKER_COMMAND'); // ``` After interpolation, the generated program contains attacker-controlled JavaScript that is executed by Node.js. This is code injection rather than ordinary malformed input because the affected values are incorporated into source code, not passed as data. ### Attack Path 1. An attacker influences the second argument supplied to `scripts/update-progress.sh`, or controls the `OPENCLAW_HOME` environment variable. 2. The attacker includes a quote followed by valid JavaScript in that value. 3. The shell expands the value into the unquoted heredoc. 4. The injected quote terminates the intended JavaScript string literal. 5. The remaining attacker-controlled text be ...[truncated 1274 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not construct JavaScript source code by interpolating shell variables. Use a quoted heredoc and pass all values as environment variables or positional arguments: ```bash if ! [[ "$DATE" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then echo '{"error":"Date must use YYYY-MM-DD format"}' >&2 exit 1 fi TIPS_FILE="$TIPS_FILE" DAY="$DAY" DATE="$DATE" node <<'NODESCRIPT' const fs = require('fs'); const path = require('path'); const tipsFile = process.env.TIPS_FILE; const day = Number.parseInt(process.env.DAY, 10); const date = process.env.DATE; if (!Number.isInteger(day) || day < 1 || day > 7) { throw new Error('Invalid day'); } if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) { throw new Error('Invalid date'); } // Continue state processing using these values as data. NODESCRIPT ``` Additional hardening measures: 1. Validate that the date is both syntactically valid and represents a real calendar date. 2. Treat `OPENCLAW_HOME` as untrusted configuration and resolve it to an expected absolute directory. 3. Reject unexpected control characters in paths and arguments. 4. Avoid embedding any environment-derived value into generated source code. 5. Add regression tests containing quotes, backslashes, newlines, JavaScript syntax, and shell metacharacters. 6. Run the Skill with only the filesystem and network privileges required for reminder operation. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill is presented as a benign reminder/onboarding workflow, but the content directs autonomous external fetching, script execution, and content extraction without clearly scoped permissions or matching implementation guarantees. Description-behavior mismatch is dangerous because it can hide materially riskier behavior from users and reviewers, increasing the chance of silent network activity or misuse under a trusted-looking label.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The README states the skill will detect the user's language and set both output language and URL language accordingly. This imposes a language choice automatically rather than offering the user a selection or opt-in, which matches the locale-policy violation criteria.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README explicitly describes automatic network fetches from botlearn.ai and writes to a progress-state file, but does not clearly warn users that the skill will perform external requests and persist state automatically. In an agent-skill context, undisclosed outbound network access and state changes can affect privacy, auditing, and user expectations, especially when triggered by heartbeat rather than an explicit user action.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill explicitly instructs the agent to run scripts, use shell tools, and perform network fetches, but it does not declare any corresponding tool scope or permissions. This creates a governance gap: a host may permit broader execution than users expect, and reviewers cannot reliably assess or constrain the skill's capabilities from metadata alone.

Vague Triggers

Medium
Confidence
94% confidence
Finding
Broad English trigger phrases such as 'quickstart', 'next day', and 'daily reminder' are likely to match ordinary conversation unrelated to this skill. Because the skill then autonomously performs setup, script execution, and web fetching, accidental activation can lead to unintended network access and state changes without meaningful user intent.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The Chinese trigger phrases are similarly generic, including terms like '今日提醒', '学习进度', and '跳到下一天', which can appear in normal conversation. In a skill that performs autonomous actions, ambiguous multilingual triggers increase the chance of unintended invocation and background network/script activity.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill tells the agent to fetch external pages autonomously and explicitly says not to ask the user to visit URLs manually. Silent network activity is risky because it can disclose usage patterns, retrieve untrusted remote content for summarization, and surprise users who did not knowingly consent to external requests.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The metadata config says the heartbeat memory file is 'memory/botlearn-reminder.json' while the documentation says state is stored at 'memory/botlearn-tips.json'. This inconsistency can cause the skill to read and write different files than intended, leading to broken reminder logic, state confusion, or accidental overwriting/exposure of unrelated data.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The manifest tags and capabilities explicitly indicate "chinese" and "chinese-summarization", suggesting the skill may constrain output language behavior. There is no visible indication in this file that users are offered a language choice or that the locale restriction is narrowly justified.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The manifest registers broad natural-language triggers such as 'quickstart', 'learning progress', 'next day', and similar Chinese phrases that could match ordinary user speech outside a clear BotLearn context. In a heartbeat-enabled skill that tracks progress and fetches dynamic content, accidental invocation can cause unintended state changes, unsolicited reminders, or external fetch activity without an explicit user request.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Line L014 requires all output to be in the user's detected language, and L029 repeats that presentation must be in OUTPUT_LANG. This imposes a language-selection policy based on detection rather than explicit user choice, which matches the language/locale policy violation criteria.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill explicitly instructs execution of a shell script to update user progress, which introduces an unnecessary command-execution capability into a simple reminder workflow. Even if the current arguments are intended to be fixed values, shell-script invocation expands the attack surface and can become dangerous if any part of the parameters, environment, or script path is influenced by user input or compromised state.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
The script selects quickstart URLs using BOTLEARN_LANG and defaults to 'en', which imposes a language choice without any interactive opt-in or explicit user selection in the file. The policy requires avoiding forced language or locale behavior unless the user is given a choice or the constraint is clearly justified.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This shell/Node script writes persistent state to ~/.openclaw/memory/botlearn-tips.json on first run and again when reinitializing corrupted state, but there is no confirmation prompt, print/log message, or other user-facing disclosure that local files are being created or replaced. Because this is a code file and the operation affects user data on disk, it meets the missing-warning criterion.

Static analysis

No suspicious patterns detected.