Back to skill

Security audit

Bitcoin Daily

Security checks for vulnerabilities and agentic risk

Overview

This skill fetches public Bitcoin development sources and archives summaries as advertised, with some operational and prompt-injection risks users should understand.

Install only if you are comfortable with a daily job that fetches public Bitcoin development content and stores raw copies under ~/workspace/bitcoin-dev-archive. Treat fetched mailing-list text as untrusted, review or disable any cron schedule you enable, and periodically delete archives if you do not want indefinite retention.

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

T01 · Skill Instruction Hijacking

Warning
Location
scripts/digest.js:267
Finding
Untrusted Mailing-List Content Is Exposed to Agent Instruction Hijacking<![CDATA[ ## Vulnerability Details **File Location**: `scripts/digest.js:267-272` **Vulnerability Type**: Prompt injection through untrusted remote content **Risk Level**: Medium ### Vulnerable Code ```js const preview = t.content .replace(/\s+/g, ' ') .substring(0, 500) .trim(); lines.push(`${i + 1}. **${t.title}** — ${preview}...`); lines.push(` [Thread](${t.url})`); ``` ### Technical Analysis The title and body of each remotely fetched mailing-list thread are incorporated directly into the generated Markdown digest. The only processing applied to the body is whitespace normalization and truncation; there is no trust-boundary marker, instruction filtering, or separation between remote content and instructions intended for the Agent. `SKILL.md` directs the Agent to summarize the fetched material. Consequently, text written by an external mailing-list participant enters the Agent's context as part of the material it is expected to process. An attacker can construct a title or opening paragraph containing instruction-like text, such as requests to disregard the summarization task, disclose contextual information, alter output, or invoke tools. This does not independently grant code execution. Successful exploitation depends on the host Agent's prompt-injection defenses and available tools, but the skill creates a direct untrusted-content-to-Agent channel. ### Attack Path 1. An attacker publishes or contributes to a thread on a supported Bitcoin Development mailing-list source. 2. The attacker places adversarial instructions in the thread title or within the beginning of its content. 3. The malicious thread appears among the first ten threads discovered by `fetchMailingList()`. 4. `fetchThread()` downloads and extracts the attacker-controlled text. 5. `generateSummary()` embeds up to 500 characters of that text and the complete title directly into the generated digest. 6. The Agent processes this digest while following the skill's instruction to ...[truncated 734 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly classify all fetched mailing-list and GitHub content as untrusted data in `SKILL.md`. 2. Add an Agent-facing instruction stating that commands, policies, requests, or tool-use directions found in fetched content must never be followed. 3. Place remote content inside clearly delimited data blocks and keep trusted instructions outside those blocks. 4. Prefer a constrained summarization interface that accepts structured fields such as `title`, `author`, and `body`, rather than concatenating remote text into an instruction-oriented prompt. 5. Escape Markdown control characters and sanitize misleading links before displaying remote content. 6. Consider detecting and flagging instruction-like phrases in fetched content. Detection should supplement—not replace—the explicit trust boundary. 7. Apply the same controls to commit messages and author names because those fields are also remotely controlled. 8. Restrict any Agent tools available during summarization to the minimum required and require confirmation for sensitive actions. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/digest.js:374
Finding
Archive Read Command Permits Directory Traversal Outside the Archive Root<![CDATA[ ## Vulnerability Details **File Location**: `scripts/digest.js:374-380` **Vulnerability Type**: Path traversal and arbitrary local file read **Risk Level**: Low ### Vulnerable Code ```js const dateStr = args[1]; if (!dateStr) { console.error('Usage: digest.js read <YYYY-MM-DD>'); process.exit(1); } const summaryPath = path.join(ARCHIVE_DIR, dateStr, 'summary.md'); if (!fs.existsSync(summaryPath)) { console.error(`❌ No archive for ${dateStr}`); process.exit(1); } ``` The resulting path is subsequently read with: ```js console.log(fs.readFileSync(summaryPath, 'utf8')); ``` ### Technical Analysis The `read` command describes its argument as a date, but it does not validate that the value matches the expected `YYYY-MM-DD` format. The value is passed directly to `path.join()` between the archive root and the fixed filename `summary.md`. `path.join()` normalizes `..` path components rather than confining the result to the original directory. An input such as `../../target` can therefore resolve to a path outside `ARCHIVE_DIR`. If the resulting external directory contains a readable file named `summary.md`, the script prints its contents. The fixed final filename limits exploitation to files named `summary.md`, so this is not an unrestricted arbitrary-file-read primitive. Nevertheless, it violates the intended archive boundary and can disclose accessible local data in suitably named files. Symlinked directories inside the archive could create an additional route outside the archive root. ### Attack Path 1. An attacker gains the ability to influence the argument passed to `digest.js read`, directly or through an Agent invoking the documented command. 2. The attacker supplies a traversal value, for example: ```text ../../target-directory ``` 3. The script evaluates a path equivalent to: ```text ~/workspace/bitcoin-dev-archive/../../target-directory/summary.md ``` 4. `path.join()` normalizes the traversal components, producing a location ...[truncated 769 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Strictly validate the argument before using it as a path component: ```js if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { console.error('Invalid date; expected YYYY-MM-DD'); process.exit(1); } ``` 2. Validate that the date is calendar-correct rather than accepting values such as `2026-99-99`. 3. Resolve both the archive root and candidate path, then verify containment: ```js const archiveRoot = path.resolve(ARCHIVE_DIR); const summaryPath = path.resolve(archiveRoot, dateStr, 'summary.md'); if (!summaryPath.startsWith(archiveRoot + path.sep)) { console.error('Invalid archive path'); process.exit(1); } ``` 4. Use `fs.lstatSync()` or an equivalent safe traversal strategy to reject unexpected symbolic links between the archive root and the target. 5. Confirm that the final target is a regular file before reading it. 6. Apply centralized argument validation to every command that accepts dates or path-like values. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill invokes a Node.js script that fetches remote content and archives data locally, but the manifest does not declare any explicit tool scope or permissions boundaries. That makes the skill's effective capabilities opaque to reviewers and users, increasing the chance of unintended network access, environment access, or broader agent execution than expected.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill explicitly describes automatic daily cron execution and persistent archival of raw mailing-list and commit data, but it does not clearly warn users that it performs ongoing scheduled activity and writes files over time. This can surprise users, consume storage, create retention/privacy concerns, and cause the agent to continue operating beyond a one-time user request.

External Transmission

Medium
Category
Data Exfiltration
Content
const ARCHIVE_DIR = path.join(process.env.HOME, 'workspace', 'bitcoin-dev-archive');
const GROUPS_URL = 'https://groups.google.com/g/bitcoindev';
const GITHUB_API = 'https://api.github.com/repos/bitcoin/bitcoin/commits';

// ── Helpers ──
Confidence
60% 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
89% confidence
Finding
The mailing-list fetch path invokes a child Node.js process via execSync even though the same task can be performed in-process. Introducing shell/process-spawning capability unnecessarily broadens the attack surface, increases operational risk, and can become dangerous if command construction is later modified to include untrusted input or if execution environment assumptions change.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The script stores full fetched thread contents, commit data, and generated summaries under a persistent archive directory, while the skill description emphasizes fetching and summarizing recent activity rather than long-term raw-data retention. Persistently archiving third-party content expands the data footprint and retention risk, especially if mailing-list posts contain personal data or sensitive discussion details that users would not expect to be stored locally.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/digest.js:62

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/digest.js:12