Back to skill

Security audit

News Digest

Security checks for vulnerabilities and agentic risk

Overview

This is a real news-digest automation skill, but its scheduled-run scripts handle secrets and configuration unsafely enough to require careful review before installation.

Review this skill before installing, especially if you plan to enable cron or the bootstrap hook. Use only a dedicated skill-specific secrets file, avoid ~/.env fallbacks, do not paste .env contents into chats or logs, and treat stored feedback/config values as untrusted until the unsafe parsing and validation issues are fixed.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (6)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cron-trigger.sh:41
Finding

Arbitrary Shell Code Execution Through Unsafe .env Sourcing

Content
View full analysis
/tmp/news-digest-executed ``` 3. The user installs the documented cron entry. 4. At the next scheduled run, `cron-trigger.sh` sources the file. 5. The injected command executes before configuration validation or the `openclaw` invocation. ### Impact Assessment Successful exploitation provides arbitrary command execution as the account owning the cron entry. The attacker could read that account's files and environment variables, steal Tavily or Xpoz credentials, al ...[truncated 232 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cron-trigger.sh:67
Finding

JavaScript Injection in Cron Slot Lookup

Content
View full analysis
s.name === '$1'); if (!slot) { process.exit(1); } console.log(JSON.stringify(slot)); " 2>/dev/null } list_slots() { node -e " const config = JSON.parse(require('fs').readFileSync('$CONFIG_FILE', 'utf-8')); config.slots.forEach(s => console.log(s.name + '|' + (s.hour ?? 0) + '|' + (s.window ? s.window[0] : 0) + '|' + (s.window ? s.window[1] : 23) + '|' + (s.label ?? s.name) + '|' + (s.topic ?? ''))); " 2>/dev/null } ``` The slot configuration accepts unrestricted names: ```javascript if (!opts.name) { console.error("Error: --name is required for set-slot."); process.exit(1); } // ... const slot = { name: opts.name, time: opts.time ?? existing.time ?? "08:00", // ... }; ``` ### Technical Analysis `$CONFIG_FILE` and the function argument `$1` are interpolated directly into JavaScript source passed to `node -e`. Neither value is encoded as a JavaScript string nor passed through `process.argv`. A slot name containing a quote and JavaScript statements can terminate the intended string literal and inject arbitrary JavaScript. Because `manage-config.mjs` does not constrain slot names, a crafted name can be persisted in `config.json` and later passed to the cron trigger. `CONFIG_FILE` is also derived from `NEWS_DIGEST_DATA_DIR`. A quote in that environment-controlled path can similarly alter the generated JavaScript. ### Attack Path 1. An attacker persuades the agent or user to create a slot whose name contains JavaScript syntax, or modifies `config.json` through another available write channel. 2. For example, a crafted slot name can close the quoted comparis ...[truncated 891 chars]
Remediation
View remediation
entry.name === slotName); if (!slot) process.exit(1); console.log(JSON.stringify(slot)); ' "$CONFIG_FILE" "$1" ``` - Prefer a dedicated `.mjs` helper rather than dynamically generated JavaScript. - Validate slot names at creation and use. A conservative format such as `^[A-Za-z0-9_-]{1,64}$` is sufficient for identifiers. - Validate `NEWS_DIGEST_DATA_DIR` as a filesystem path and do not embed it in executable source. - Treat manually edited `config.json` as untrusted input and validate its schema before use. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/add-feedback.mjs:39
Finding

Directory Traversal and Out-of-Scope File Write in Feedback Storage

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/query.mjs:47
Finding

Directory Traversal Enables Reads of Arbitrary JSON Files

Content
View full analysis
Remediation
View remediation

T01 · Skill Instruction Hijacking

Warning
Location
scripts/keyword-suggester.mjs:250
Finding

Indirect Prompt Injection Through Untrusted Feedback in AI Prompt Templates

Content
View full analysis
f.text).join("; ")} - Negative signals (${analysis.negativeSignals.length}): ${analysis.negativeSignals.map(f => f.text).join("; ")} Based on this feedback, generate keyword adjustment suggestions. Output format (JSON): { "suggestions": [ { "action": "add|remove|increase_weight|decrease_weight", "keyword": "defi", "reason": "User requested more DeFi content", "priority": "high|medium|low" } ], "summary": "Brief explanation of the recommended changes" } ` ); ``` ### Technical Analysis Free-form feedback text is inserted directly into a natural-language prompt intended for AI analysis. There is no trust-boundary marker, escaping strategy, or instruction telling the receiving agent that the inserted text is data and must not be followed as instructions. A feedback entry can therefore contain text such as an instruction to ignore the surrounding task, reveal context, invoke tools, or modify configuration. If the generated template is passed to an agent as instructions, the feedback can compete with the intended prompt and influence subsequent actions. This is an indirect prompt-injection weakness rather than traditional code injection. Exploitability depends on the generated output being consumed by an AI agent and on who can submit feedback. ### Attack Path 1. A malicious or compromised feedback source submits text containing agent-directed instructions. 2. `add-feedback.mjs` stores the text without restrictions ...[truncated 908 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/env-check.mjs:16
Finding

API Credential Information Exposed by Diagnostic Guidance and Prefix Logging

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (37)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 211)May include surrounding context.

md
- `fetch-tavily.mjs` = +1 `tavily_search`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 213)May include surrounding context.

md
- `fetch-hackernews.mjs` = +1 `hackernews`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 267)May include surrounding context.

md
When a user asks to **modify the schedule or topics**, use `manage-config.mjs`:

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 311)May include surrounding context.

md
Usage is recorded automatically when `store-push.mjs` receives a `usage` field.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/setup-guide.md (reported line 31)May include surrounding context.

Create a .env file in your OpenClaw workspace (this is the preferred location for cron compatibility):

bash
# ~/.openclaw/workspace/.env
TAVILY_API_KEY=tvly-xxxxxxxxxxxxx
XPOZ_API_KEY=your-xpoz-key

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/setup-guide.md (reported line 167)May include surrounding context.

Create a .env file in your OpenClaw workspace (this is the preferred location for cron compatibility):

bash
# ~/.openclaw/workspace/.env
TAVILY_API_KEY=tvly-xxxxxxxxxxxxx
XPOZ_API_KEY=your-xpoz-key

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The .env-loading logic enables credential access by importing secrets from general-purpose user locations into the skill process. Because source executes the file in the shell, an attacker who can influence one of those files can both read secrets and run arbitrary commands under the cron job's privileges.

Content

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

sh
esac
done

# --- Load .env ---
ENV_CANDIDATES=(
  "$HOME/.openclaw/workspace/.env"
  "$SKILL_ROOT/.env"

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

Using $HOME/.openclaw/workspace/.env as an automatic credential source broadens trust to a workspace-level file unrelated to this skill alone. That creates a cross-skill secret exposure boundary failure and allows any compromise or misconfiguration of that shared file to affect this cron-triggered process.

Content

Scanner excerpt · scripts/cron-trigger.sh (reported line 43)May include surrounding context.

sh
# --- Load .env ---
ENV_CANDIDATES=(
  "$HOME/.openclaw/workspace/.env"
  "$SKILL_ROOT/.env"
  "$HOME/.env"
)

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

Loading $SKILL_ROOT/.env is less concerning than loading home-directory files, but sourcing it still executes shell code and may expose credentials without validation. If the skill package or directory contents are modified by an attacker, this becomes a code execution and secret-handling risk.

Content

Scanner excerpt · scripts/cron-trigger.sh (reported line 44)May include surrounding context.

sh
# --- Load .env ---
ENV_CANDIDATES=(
  "$HOME/.openclaw/workspace/.env"
  "$SKILL_ROOT/.env"
  "$HOME/.env"
)

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

Automatically sourcing $HOME/.env is especially risky because it is a broad, user-scoped secret store that may contain credentials unrelated to this skill. This unnecessarily expands the blast radius of the cron job and can leak or misuse sensitive tokens from other applications.

Content

Scanner excerpt · scripts/cron-trigger.sh (reported line 45)May include surrounding context.

sh
ENV_CANDIDATES=(
  "$HOME/.openclaw/workspace/.env"
  "$SKILL_ROOT/.env"
  "$HOME/.env"
)

ENV_LOADED=""

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/env-check.mjs (reported line 12)May include surrounding context.

js
const SKILL_ROOT = join(__dirname, "..");

const ENV_CANDIDATES = [
  join(process.env.HOME ?? "~", ".openclaw", "workspace", ".env"),
  join(SKILL_ROOT, ".env"),
  join(process.env.HOME ?? "~", ".env"),
];

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/env-check.mjs (reported line 13)May include surrounding context.

js
const SKILL_ROOT = join(__dirname, "..");

const ENV_CANDIDATES = [
  join(process.env.HOME ?? "~", ".openclaw", "workspace", ".env"),
  join(SKILL_ROOT, ".env"),
  join(process.env.HOME ?? "~", ".env"),
];

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/env-check.mjs (reported line 14)May include surrounding context.

js
const SKILL_ROOT = join(__dirname, "..");

const ENV_CANDIDATES = [
  join(process.env.HOME ?? "~", ".openclaw", "workspace", ".env"),
  join(SKILL_ROOT, ".env"),
  join(process.env.HOME ?? "~", ".env"),
];

Credential Access

High
Category
Privilege Escalation
Confidence
80% confidence
Finding

The script logs the full path of the discovered .env file to stdout, revealing where secrets are stored on the local system. Although it does not print secret contents, exposing sensitive file locations can aid reconnaissance, leak usernames/home-directory structure, and increase risk when output is collected in shared logs.

Content

Scanner excerpt · scripts/env-check.mjs (reported line 35)May include surrounding context.

js
console.log(`- **Data dir**: ${getDataDir()} (${existsSync(getDataDir()) ? "exists" : "will be created on first use"})`);

const envFile = ENV_CANDIDATES.find((p) => existsSync(p));
console.log(`- **.env file**: ${envFile ? `found at ${envFile}` : "not found"}`);

const config = loadConfig();
if (config && config.slots) {

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill instructs the agent to execute local Node scripts, access environment variables, and call external services, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates an authorization gap where the runtime may grant broader capabilities than reviewers or users expect, increasing the risk of unintended network access, secret exposure, or filesystem actions through the referenced scripts.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The setup flow hardcodes the agent's user-facing questions in Chinese, which implicitly requires a specific language during onboarding. The file does not first ask the user for a preferred language or state that Chinese is optional, so this conflicts with a language/locale choice policy.

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 · references/setup-guide.md (reported line 26)May include surrounding context.

md
## Environment Variables

### Create a `.env` File

Create a `.env` file in your OpenClaw workspace (this is the preferred location for cron compatibility):

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This markdown file applies to SQP-2, and it documents handling sensitive credentials (TAVILY_API_KEY and XPOZ_API_KEY). The guide provides exact storage locations and example secrets but does not warn users about keeping the file private, excluding it from version control, or restricting permissions, which is a missing user warning for privacy-sensitive behavior.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% 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 · references/setup-guide.md (reported line 179)May include surrounding context.

Step 2: Add cron entries

bash
crontab -e

Add entries for each slot in your config. The times should match:

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% 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 · references/setup-guide.md (reported line 199)May include surrounding context.

Step 3: Verify cron is running

bash
crontab -l

Auto-Detection

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% 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 · references/setup-guide.md (reported line 294)May include surrounding context.

Step 3: Verify cron is running

bash
crontab -l

Auto-Detection

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script automatically sources environment files from broad user-level locations such as $HOME/.openclaw/workspace/.env and $HOME/.env, not just the skill-local configuration. In Bash, source executes shell content rather than merely parsing key/value pairs, so a malicious or unexpected .env file can inject commands or expose unrelated secrets to this skill's execution context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Sensitive environment files are loaded implicitly and silently during cron execution, giving the user little visibility that secrets from unrelated locations may be imported. This increases the chance of accidental secret exposure, privilege confusion, or execution of attacker-controlled shell content from those files.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script prints the first four characters of API keys to stdout, which is still credential material and can leak into terminal logs, CI logs, screenshots, or support transcripts. Even partial secret disclosure weakens secrecy, helps correlate which key is in use, and provides attackers with validating information about sensitive tokens.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/extract-tavily.mjs (reported line 24)May include surrounding context.

js
process.exit(1);
}

const resp = await fetch("https://api.tavily.com/extract", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ api_key: apiKey, urls }),