Back to skill

Security audit

Paragraph

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it has review-worthy risks around public publishing, subscriber data uploads, and sending credentials to a configurable API host.

Install only if you are comfortable giving the skill a Paragraph API key that can publish posts and manage subscribers. Keep PARAGRAPH_API_BASE_URL unset or pinned to the official Paragraph API, review all content before invoking post creation, and only import subscriber CSVs from intentional, verified paths with proper consent for email or wallet-address processing.

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

T09 · Insecure Skill Coding Practices

Error
Location
skill.js:9
Finding

Paragraph Credentials and Sensitive Request Data Can Be Redirected to an Arbitrary Host

Content
View full analysis
{ if (params[key] !== undefined && params[key] !== null) { url.searchParams.append(key, String(params[key])) } }) const headers = { "Authorization": `Bearer ${apiKey}` } let fetchBody = null if (body) { headers["Content-Type"] = "application/json" fetchBody = JSON.stringify(body) } if (options.rawBody) { fetchBody = options.rawBody Object.assign(headers, options.headers) } else if (options.formData) { fetchBody = options.formData // Don't set Content-Type; fetch will set boundary } // Set up abort controller for timeout const controller = new AbortController() const timeoutMs = options.timeout || 30000 // default 30 seconds (POSTs can be slow) const timeoutId = setTimeout(() => controller.abort(), timeoutMs) try { const response = await fetch(url.toString(), { method, headers, body: fetchBody, signal: controller.signal }) ``` The CSV import path independently uses the same configurable destination: ```javascript const url = new URL(`${API_BASE}/v1/subscribers/import`) url.searchParams.append('sendWelcomeEmail', sendWelcomeEmail) const formData = new FormData() formData.append('file', csvBuffer, 'subscribers.csv') const response = await fetch(url.toString(), { method: "PO ...[truncated 2802 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
skill.js:439
Finding

Subscriber Import Permits Unrestricted Local File Reads and Network Uploads

Content
View full analysis
{ if (!csvPath) throw new Error("csvPath is required") const fs = await import('fs') const csvBuffer = fs.readFileSync(csvPath) // Build URL with query param const url = new URL(`${API_BASE}/v1/subscribers/import`) url.searchParams.append('sendWelcomeEmail', sendWelcomeEmail) const formData = new FormData() formData.append('file', csvBuffer, 'subscribers.csv') const response = await fetch(url.toString(), { method: "POST", headers: { "Authorization": `Bearer ${process.env.PARAGRAPH_API_KEY}` // Content-type (with boundary) set automatically by fetch when using FormData }, body: formData }) ``` ### Technical Analysis The `csvPath` tool argument is passed directly to `fs.readFileSync()`. The Skill performs no validation that the resolved target: - Is inside an approved import or workspace directory. - Is a regular file. - Has a CSV extension or valid subscriber CSV structure. - Is below the documented maximum size. - Is not reached through path traversal or a symbolic link. - Was explicitly selected and confirmed by the user. Consequently, the operation inherits all filesystem-read permissions of the OpenClaw process rather than limiting access to files necessary for subscriber import. The entire file is loaded synchronously into memory and then placed into a multipart network request. This is especially dangerous in an agent context because untrusted instructions can attempt to induce a tool call with a sensitive absolute path or traversal path. When combined with the unrestricted API destination vulnerability, the file is delivered directly to an attacker-controlled server. Even with the legitimate Paragraph desti ...[truncated 1998 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

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 · README.md (reported line 29)May include surrounding context.

md
## Prerequisites

1. **Paragraph Account**: Create an account at [paragraph.com](https://paragraph.com)
2. **API Key**: Generate one in Account Settings → Integrations
3. **Node.js 19+**: Required for native fetch (OpenClaw uses Node 24+)

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 · README.md (reported line 81)May include surrounding context.

Agent Prompt Examples

Create a blog post from research findings

text
You are a content creator. Use the paragraph skill to publish a new blog post
based on today's research notes. Title: "AI Trends 2026", markdown content

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The example prompt encourages creating a post from research notes without a clear warning that the tool publishes immediately and may create an irreversible onchain record. For agent-driven workflows, omission of that warning is risky because users may treat the example like a safe drafting action and accidentally publish sensitive, inaccurate, or unreviewed content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The subscriber add/import flows process personal data such as email addresses and wallet addresses, and may also trigger welcome emails, but the README does not prominently warn about privacy, consent, or transmission to a third-party service. In an agent context, this increases the chance that a user bulk-imports contacts without understanding the privacy and notification consequences.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The README gives workflow guidance that contradicts the rest of the document: it recommends draft/update publishing using published: false and paragraph_updatePost, while elsewhere it explicitly says updates are unsupported and posts publish immediately onchain. In an agent skill, misleading operational docs are dangerous because they can cause users or higher-level agents to assume review gates exist when the actual action is immediate and potentially irreversible.

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 · README.md (reported line 473)May include surrounding context.

md
1. Writer generates markdown draft
2. Publisher Agent calls `paragraph_createPost` with `published: false`
3. Review/approval step
4. Publish: `paragraph_updatePost` with `published: true` OR create with `published: true`
5. If tokenizing: capture coin ID from response, track via `paragraph_getCoin` and `paragraph_listCoinHolders`
6. Analytics: correlate engagement (views, holders) with content performance

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill states that posts are published onchain immediately upon creation, but it does not elevate the consequence that such publication may be effectively irreversible, publicly permanent, and difficult to correct after submission. In an automation context, this omission can cause accidental disclosure of sensitive, confidential, or legally risky content through a single mistaken tool call.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill explicitly supports adding subscribers, tagging them, and importing bulk CSVs containing email addresses and wallet-linked audience data, but it does not present a clear privacy/compliance warning where those features are described. This creates a real risk of misuse, unauthorized contact import, or mishandling regulated personal data, especially because wallet addresses can be linkable identifiers in a Web3 context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The import tool reads an arbitrary local path from csvPath and uploads its contents to a remote API using the skill's credentials, with no path restrictions, validation, or explicit confirmation gate. In an agent setting, this increases the risk of unintended local file exfiltration if an upstream prompt, tool caller, or compromised workflow supplies a sensitive file path instead of a subscriber CSV.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The troubleshooting section says tags must be an array of strings, but the documented paragraph_createPost parameters define categories as the tag field. This is contradictory guidance that can mislead users about what the skill actually accepts.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documentation states that waitForProcessing defaults to false, but the inline comment inside the function also claims the subsequent polling branch is 'default' and says 'If waitForProcessing is true (default)'. The implementation clearly sets waitForProcessing = false, so the comment at L272 misrepresents what the code actually does.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
skill.js:9

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
SKILL.md:222