Back to skill

Security audit

Knowledge Management

Security checks for vulnerabilities and agentic risk

Overview

This skill organizes local memory files, but it needs review because indexing can execute JavaScript from markdown metadata and cleanup can permanently delete local knowledge files.

Install only after reviewing the code or using it in a dedicated test workspace. Avoid `km summarize` on folders containing markdown you did not create or trust, and avoid `km cleanup` or scheduled cleanup until deletion is replaced with a recoverable archive/quarantine or requires explicit confirmation after a dry-run review.

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

T09 · Insecure Skill Coding Practices

Error
Location
index-local.js:571
Finding
Arbitrary JavaScript Execution Through Unsafe Frontmatter Parsing<![CDATA[ ## Vulnerability Details **File Location**: `index-local.js:571-583` **Vulnerability Type**: Unsafe evaluation of file-controlled data **Risk Level**: High ### Vulnerable Code ```js const tagsMatch = fm.match(/^tags:\s*\[([\s\S]*?)\]/m); if (titleMatch) entryData.title = titleMatch[1]; if (domainMatch) entryData.domain = domainMatch[1]; if (confidenceMatch) entryData.confidence = confidenceMatch[1]; if (impactMatch) entryData.impact = impactMatch[1]; if (tagsMatch) { try { const tagsArray = eval(`[${tagsMatch[1]}]`); // Safe since we control the format entryData.tags = tagsArray.join(', '); } catch (e) { entryData.tags = tagsMatch[1]; } } ``` ### Technical Analysis The `generateIndex` function scans every Markdown file in each configured content-type directory and extracts the contents of the `tags` frontmatter field. It then passes the extracted text directly to JavaScript `eval`. The claim that the input format is controlled is incorrect because these directories can contain manually created, imported, synchronized, or otherwise attacker-influenced Markdown files. The cleanup implementation explicitly recognizes that files can exist without being tracked by the Skill's state. A tags field can contain a valid JavaScript expression rather than a simple string list. For example, an expression using an immediately invoked function can execute before returning a value acceptable to the surrounding array. Execution occurs in the Node.js process and can access Node globals and modules available to the application. ### Attack Path 1. An attacker gains the ability to place or modify a Markdown file in an output content directory such as `Research/`, `Decision/`, or `Insight/`. 2. The attacker creates frontmatter containing a malicious JavaScript expression in the `tags` array. 3. A user or scheduled workflow runs `km summarize`. 4. `generateIndex` reads the malicious file and captures the tags content. 5. The captured content is p ...[truncated 819 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `eval` entirely. Structured data must never be interpreted as JavaScript. 2. Parse frontmatter using a maintained YAML parser configured with a safe schema that does not instantiate arbitrary types or functions. 3. If an additional dependency is undesirable, implement strict parsing that only accepts a comma-separated list of quoted strings. 4. Validate that the parsed value is an array and that every element is a string with an enforced maximum length. 5. Reject malformed tags instead of attempting to evaluate or loosely interpret them. 6. Escape Markdown table metacharacters before inserting frontmatter values into generated indexes. 7. Add security tests using malicious tags expressions to verify that summarization cannot cause code execution. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index-local.js:473
Finding
Destructive Cleanup Deletes Legitimate Files When State Is Missing or Corrupt<![CDATA[ ## Vulnerability Details **File Location**: `index-local.js:352-360` and `index-local.js:473-518` **Vulnerability Type**: Fail-open state handling followed by permanent file deletion **Risk Level**: Medium ### Vulnerable Code ```js loadState() { try { if (fs.existsSync(STATE_FILE)) { return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8')); } } catch (e) { warning(`Failed to load state: ${e.message}`); } return {}; } ``` ```js cleanupOrphans(dryRun = false) { info('Scanning for orphan files...'); // Build set of all files under content type folders const allFiles = new Set(); for (const ct of CONTENT_TYPES) { const folder = path.join(this.workspace, ct); if (fs.existsSync(folder)) { const files = fs.readdirSync(folder).filter(f => f.endsWith('.md')); for (const f of files) { allFiles.add(path.join(folder, f)); } } } // Files tracked in state const trackedFiles = new Set(Object.values(this.state)); // Orphans = files on disk not in state const orphanFiles = [...allFiles].filter(f => !trackedFiles.has(f)); info(`Found ${allFiles.size} knowledge files on disk`); info(`State tracks ${trackedFiles.size} files`); info(`Found ${orphanFiles.length} orphan files to archive`); if (dryRun) { info('DRY-RUN: would delete these files:'); for (const f of orphanFiles) info(` - ${f}`); return; } for (const f of orphanFiles) { try { fs.unlinkSync(f); this.logAction('ARCHIVED_ORPHAN', `deleted ${f}`); } catch (e) { error(`Error deleting ${f}: ${e}`); this.logAction('ARCHIVE_ERROR', `file ${f}: ${e.message}`); } } } ``` ### Technical Analysis State-loading errors are handled by returning an empty object. The cleanup algorithm treats every Markdown file not represented in that object as an orphan and permanently removes it with `fs.unlinkSync`. Consequently, an absent, malformed, truncated, unreadable, or incompatible ...[truncated 1904 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail closed: abort cleanup if the state file is absent, malformed, unreadable, or fails schema validation. 2. Distinguish an intentionally initialized empty state from a state-loading failure. 3. Store a provenance marker in every generated file and only clean files carrying a valid marker created by this Skill. 4. Move orphan candidates into a recoverable quarantine directory instead of permanently deleting them. 5. Require explicit confirmation for non-dry-run cleanup, with a separate force option for unattended use. 6. Refuse cleanup when the proposed deletion count is anomalously high or exceeds a configurable threshold. 7. Write state atomically using a temporary file followed by rename, and retain a validated backup. 8. Remove `--cleanup` from documented cron examples unless safe state validation and recoverable deletion are implemented. 9. Rename deletion log events accurately; do not report permanent deletion as archival. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
index-local.js:462
Finding
Dry-Run Mode Performs Undocumented Filesystem Writes<![CDATA[ ## Vulnerability Details **File Location**: `index-local.js:368-373` and `index-local.js:462-469` **Vulnerability Type**: Violation of dry-run safety guarantees **Risk Level**: Low ### Vulnerable Code ```js logAction(action, details) { const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC'; const logEntry = `- ${timestamp}: ${action} - ${details}\n`; fs.mkdirSync(MEMORY_DIR, { recursive: true }); fs.appendFileSync(SYNC_LOG, logEntry); info(`${action}: ${details}`); } ``` ```js if (dryRun) { this.logAction('DRY-RUN', `Would create: ${filename} (${meta.content_type})`); info(` Title: ${processedEntry.title}`); info(` Domain: ${meta.domain}, Confidence: ${meta.confidence_score}, Impact: ${meta.impact}`); info(` Tags: ${meta.tags.join(', ')}`); info(` Body length: ${(processedEntry.body || '').length}`); return null; } ``` ### Technical Analysis The documented purpose of `--dry_run` is to preview behavior without creating files. However, the dry-run branch calls `logAction`, which creates a directory recursively and appends to the synchronization log. As a result, dry-run execution is not read-only. It can alter directory timestamps, create the memory directory, create the log file, and append one record for every prospective entry. This is not an arbitrary-write vulnerability because the destinations are derived from the configured output location. It nevertheless violates the expected safety property of a preview mode and can cause unintended modifications in sensitive or version-controlled workspaces. ### Attack Path 1. A user invokes `km sync --dry_run`, expecting no filesystem changes. 2. For each entry that would be created, `storeEntry` calls `logAction`. 3. `logAction` creates the configured directory if necessary. 4. It creates or appends to `local-sync-log.md`. 5. The workspace is modified despite dry-run mode. ### Impact Assessment The impact is limited to unexpected director ...[truncated 354 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. In dry-run mode, send preview events only to standard output or standard error. 2. Do not call `mkdirSync`, `appendFileSync`, or any other mutating operation during a dry run. 3. If persistent dry-run auditing is required, place it behind a separate explicit option such as `--write-preview-log`. 4. Update documentation if any side effects are intentionally retained. 5. Add automated tests that snapshot the filesystem before and after dry-run execution and verify that no files, directories, or metadata are changed. ]]>
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (14)

Missing User Warnings

High
Confidence
95% confidence
Finding
The cron examples automate `km sync --days_back 7 --cleanup`, causing recurring unattended deletion based on sync-state logic. If the state file becomes stale, corrupted, or parsing/classification changes, legitimate files could be repeatedly deleted without user review, making the impact higher than one-off manual cleanup.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description frames the skill as simple organization/classification, but the documented behavior includes deletion, persistent state/log creation, index generation, and processing of additional files. This mismatch can mislead users into granting trust to a skill that performs materially broader and more destructive local actions than its summary suggests.

Ae1

High
Category
analysis-evasion
Content
Customize by editing the `EntryClassifier` class in `index-local.js`.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The documentation and log language say orphan files will be 'archived', but the implementation uses fs.unlinkSync() to permanently delete them. This mismatch is dangerous because users may invoke cleanup expecting reversible behavior and instead lose data irreversibly.

Session Persistence

Medium
Category
Rogue Agent
Content
No API keys needed! Just ensure your workspace structure exists:

```bash
mkdir -p ~/.openclaw/workspace/{Research,Decision,Insight,Lesson,Pattern,Project,Reference,Tutorial}
```

The skill will create folders automatically if missing.
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.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README documents a `--cleanup` mode that deletes files deemed orphaned, but it does not clearly emphasize that deletion may be permanent or that misclassification/state corruption could remove legitimate data. In a knowledge-management skill that operates on a user's local workspace, destructive operations are security-relevant because users may run them routinely without understanding recovery implications.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The example shows `km cleanup` directly after `km cleanup --dry_run`, but does not include a caution that the non-dry-run variant will delete files from the local knowledge base. Documentation that normalizes a destructive command without warning increases the chance of accidental data loss, especially for users copying commands verbatim.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documents use of the OPENCLAWORKSPACE environment variable and therefore depends on ambient environment-derived behavior, but it does not declare any explicit tool scope or permission boundary. That makes file selection and write targets less transparent to users and reviewers, increasing the risk of unintended access to local data when the environment is manipulated or misunderstood.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill exposes a cleanup feature that removes orphaned files, but the warning language is weak relative to the destructive effect. Users may invoke cleanup assuming it is harmless maintenance, when incorrect state or path resolution could cause irreversible deletion of local files in the managed output tree.

Session Persistence

Medium
Category
Rogue Agent
Content
1. `--output-dir <path>` CLI argument (relative to workspace or absolute)
2. Default: `<workspace>/memory/KM`

The skill will create the output directory and all content-type folders automatically.

If you want to pre-create:
```bash
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.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill's stated purpose is organization and classification, but it also performs destructive file deletion in cleanupOrphans(). It scans type folders and unlinks any markdown file not present in its state file, which can remove legitimate knowledge files if state is missing, corrupted, stale, or output paths are misconfigured.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
Cleanup mode deletes orphaned markdown files immediately without a strong user-facing warning or confirmation at the point of deletion. In this skill's context, users are managing local knowledge stores, so silent permanent deletion increases the risk of accidental data loss from normal use, especially when state tracking is incomplete.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The documentation states that the skill automatically creates directories and writes organized files, but it does not foreground that local filesystem contents will be modified. While expected for this skill type, the lack of a prominent safety notice can still surprise users and lead to unintended changes in sensitive workspaces.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
The cron integration example specifies `--tz "Asia/Singapore"`, which imposes a locale-specific setting in natural-language guidance. The documentation does not indicate that users should choose their own timezone or that this locale is required for a region-specific use case.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
index-local.js:584