Back to skill

Security audit

openclaw-crm

Security checks for vulnerabilities and agentic risk

Overview

This local CRM is mostly purpose-aligned, but it publishes sensitive CRM records as agent-readable Markdown without enough boundaries, so it needs review before installation.

Install only if you are comfortable with CRM data being stored locally and exported into Markdown files for other agents. Treat the interchange directory as sensitive, do not feed it to agents that may follow embedded instructions, and prefer adding redaction, escaping, and explicit opt-in controls before using it with real customer data.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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)

T02 · Agent Memory Poisoning

Error
Location
src/interchange.js:89
Finding

Persistent Cross-Agent Prompt Injection Through Generated Markdown

Content
View full analysis
t.tag); const meta = { ...BASE_META, type: 'detail', layer: 'state', deal_id: deal.id, tags: ['deal'] }; let content = `# ${deal.title}\n\n`; content += `**Stage:** ${deal.stage} | **Value:** $${deal.value} | **Source:** ${deal.source || 'Direct'}\n\n`; if (contact) { content += `## Contact\n- **Name:** ${contact.name}\n- **Company:** ${contact.company || '-'}\n- **Email:** ${contact.email || '-'}\n\n`; } content += `## Activities\n`; if (activities.length === 0) { content += 'No activities logged.\n'; } else { for (const a of activities) { content += `- ${a.timestamp} **${a.type}**: ${a.content}\n`; } } content ...[truncated 2331 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/followups.js:29
Finding

SQL Injection Through Interpolated Follow-Up Day Offset

Content
View full analysis
Remediation
View remediation
3650) { throw new Error('Days must be an integer between 0 and 3650'); } const stmt = db.prepare(` SELECT f.*, d.title as deal_title, c.name as contact_name, c.company as contact_company FROM follow_ups f JOIN deals d ON f.deal_id = d.id LEFT JOIN contacts c ON d.contact_id = c.id WHERE f.completed = 0 AND f.due_date <= datetime('now', ?) ORDER BY f.due_date ASC `); return stmt.all(`+${days} days`); } ``` Additional hardening measures: 1. Do not rely exclusively on CLI parsing; exported functions must enforce their own validation. 2. Reject `NaN`, infinity, negative values if unsupported, fractional values, and excessive offsets. 3. Use placeholders for every externally influenced SQL value. 4. Add direct unit tests using SQL metacharacters and malformed values. 5. Review future dynamic SQL construction to ensure only fixed, allowlisted SQL fragments are interpolated. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (23)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Run `node src/cli.js lead add "John Doe" --email john@example.com`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Run `node src/cli.js lead add "John Doe" --email john@example.com`

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The module-level documentation asserts that interchange output avoids deal values, contact information, and user data, but the same module later generates state files containing exactly that sensitive CRM content. This mismatch is dangerous because downstream users, agents, or operators may trust the documented safety boundary and exfiltrate or broadly share the generated markdown under false assumptions.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

writeDealFiles exports deal titles, stages, monetary values, source data, contact names, companies, email addresses, activity content, and tags into markdown files under the interchange tree. If interchange artifacts are consumed by other agents, synced, indexed, or exposed more broadly than the source CRM, this creates a direct sensitive-data disclosure channel and may leak commercial and personal information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README explicitly states that CRM state, including pipeline, follow-ups, and contact information, is published to Markdown for other agents to read, but it does not warn about privacy, sensitivity, or access control expectations. In a CRM context this can expose personal and commercially sensitive data through secondary artifacts that are easier to copy, index, or share than the underlying local database.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
76% confidence
Finding

The top-level description presents the tool as a local-first CRM, which implies local contact/deal management as its core function. However, the CLI also exposes explicit interchange refresh and backup/restore capabilities elsewhere in the file without that broader operational scope being reflected in the main description, creating an intent/documentation mismatch.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The restore command invokes restoreFromBackup(dbBase, file) immediately on user input and provides no confirmation, dry-run, or backup-before-restore safeguard. A mistaken path or accidental invocation can overwrite local CRM data and cause irreversible data loss, which is a real integrity risk even in a local CLI context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code persists user contact information including name, company, email, phone, and notes to storage, which affects user data and privacy. The function includes technical comments but no confirmation prompt, user-facing disclosure, or warning that personal data will be stored.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This update operation changes stored contact information, which is a user-data-affecting action. Although editing contacts is the function's purpose, the code provides no visible disclosure, confirmation, or logging to inform the user that persistent records are being changed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This function writes markdown files under the state/deals directory and includes deal value, source, contact name, company, email, and activity content. While the module header states ops files contain no user data, there is no corresponding warning, confirmation, or user-facing disclosure here that state files will persist CRM data to disk.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · src/reports.js (reported line 51)May include surrounding context.

js
LIMIT 5
  `);
  const topDeals = topDealsStmt.all(...openStages);
  let topList = '### Top Deals\n';
  if (topDeals.length === 0) {
    topList += '- No active deals\n';
  } else {

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · src/reports.js (reported line 53)May include surrounding context.

js
LIMIT 5
  `);
  const topDeals = topDealsStmt.all(...openStages);
  let topList = '### Top Deals\n';
  if (topDeals.length === 0) {
    topList += '- No active deals\n';
  } else {

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · src/reports.js (reported line 56)May include surrounding context.

js
LIMIT 5
  `);
  const topDeals = topDealsStmt.all(...openStages);
  let topList = '### Top Deals\n';
  if (topDeals.length === 0) {
    topList += '- No active deals\n';
  } else {

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · src/reports.js (reported line 71)May include surrounding context.

js
LIMIT 5
  `);
  const topDeals = topDealsStmt.all(...openStages);
  let topList = '### Top Deals\n';
  if (topDeals.length === 0) {
    topList += '- No active deals\n';
  } else {

Known Vulnerable Dependency: uuid==10.0.0 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The lockfile pins uuid to 10.0.0, and the provided advisory indicates this version is affected by a missing buffer bounds check in certain UUID generation paths when a caller supplies a buffer. While a package-lock.json entry alone does not prove the vulnerable API is exercised, inclusion of a specifically flagged vulnerable version is a real supply-chain risk because downstream code may invoke the affected functions and trigger denial of service or unexpected behavior.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 18)May include surrounding context.

json
"test": "node --test test/crm.test.js"
  },
  "dependencies": {
    "better-sqlite3": "^10.0.0",
    "commander": "^12.0.0",
    "uuid": "^10.0.0"
  }

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 19)May include surrounding context.

json
},
  "dependencies": {
    "better-sqlite3": "^10.0.0",
    "commander": "^12.0.0",
    "uuid": "^10.0.0"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 20)May include surrounding context.

json
"dependencies": {
    "better-sqlite3": "^10.0.0",
    "commander": "^12.0.0",
    "uuid": "^10.0.0"
  }
}

Known Vulnerable Dependency: uuid==10.0.0 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
91% confidence
Finding

The file specifies uuid 10.0.0, which is flagged with CVE-2026-41907 for missing buffer bounds checks in certain UUID generation paths when a buf argument is supplied. If application code or downstream code invokes the affected v3/v5/v6 APIs with attacker-influenced buffer parameters, this could trigger crashes or other unintended behavior, making it a real though low-severity dependency risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This code performs a persistent write to the activities table, which affects user data, but the file contains no user-facing disclosure, confirmation prompt, or logging indicating that a record will be stored. Although adding activities appears to be the function's purpose, there is no visible warning mechanism in this code file itself about the persistence of submitted content.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
67% confidence
Finding

This command's description is too abstract compared with the concrete side effect of invoking refreshInterchange(db), which likely writes or regenerates local files. While not necessarily unsafe, the inline documentation understates the operational impact of the command.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The backup command creates or copies database files to disk, which is a file-write operation. Although it logs success afterward, there is no prior user-facing warning, comment, or descriptive disclosure here that invoking the command will write backup files to the specified or default path.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The comment says contacts are filtered 'using FTS', which implies a full-text search mechanism, but the code actually builds a LIKE-based query over name, company, and email fields. This is an active mismatch between the documented intent and the implemented behavior, even though the security impact is limited.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.