Back to skill

Security audit

smart-auto-note

Security checks for vulnerabilities and agentic risk

Overview

This note-writing skill is mostly purpose-aligned, but it writes to a fixed local folder and requests extra messaging and scheduled-execution authority that its documentation does not clearly disclose.

Review before installing. Use it only if you are comfortable with it creating and appending Markdown files under /Users/macbook/Documents/OC_club, and prefer a revised version that uses a configurable vault path, removes message.send and the cron trigger unless implemented, avoids import-time writes, scopes confirmation state per user/session, and accurately documents the remote Mermaid dependency or bundles it locally.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
index.js:15
Finding
Process-Global Confirmation State Allows Cross-Session Data Contamination<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 15-16 and 113-140 **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: High ### Evidence ```js // Global state used for all callers let pendingState = null; let pendingArchiveState = null; ``` ```js async function main(input, context) { await ensureFiles(); // State machine 1: archive classification confirmation if (pendingArchiveState) { const reply = input.trim(); if (reply === '项目类' || reply === '其他类') { const res = await doArchive(pendingArchiveState.content, reply); pendingArchiveState = null; return res.msg; } else { pendingArchiveState = null; return '已取消归档操作。'; } } // State machine 2: content classification confirmation if (pendingState) { const reply = input.trim(); const valid = ['工作待办', '生活待办', '工作记录', '灵感']; if (valid.includes(reply)) { const fp = getFilePath(reply); await fs.appendFile(fp, formatContent(pendingState.content, reply)); pendingState = null; return `✅ 已成功记录到【${reply}】`; } else { pendingState = null; return '已取消操作。'; } } ``` ### Technical Analysis The pending confirmation state is stored in module-level variables shared by every invocation in the Node.js process. Although `main` receives a `context` parameter, that context is not used to associate pending content with a particular user, conversation, or authenticated session. In a multi-user or concurrently invoked agent runtime, one caller can therefore consume, classify, cancel, or archive content submitted by another caller. The check and subsequent state clearing are also not synchronized, which can produce race conditions during concurrent invocations. This violates session isolation and can cause private note content to cross trust boundaries. ### Attack Path 1. User A submits ambiguous or private content that receives a classification confidence below 90%. 2. Th ...[truncated 1110 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace module-level state with a map or persistent store keyed by a trusted conversation or session identifier from `context`. - Include the authenticated user identity in the key where multiple users can share a conversation namespace. - Reject confirmation messages when no pending operation exists for the current session. - Add short expiration times to all pending operations. - Clear state atomically only after the associated operation completes. - Use per-session locking or compare-and-swap semantics to prevent concurrent confirmations from consuming the same operation. - Avoid placing sensitive content in process-global mutable variables. - Add tests that interleave requests from multiple users and verify strict isolation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
metadata.json:8
Finding
Skill Requests Unnecessary Messaging and Scheduled-Execution Capabilities<![CDATA[ ## Vulnerability Details **File Location**: `metadata.json`, lines 8-17; `index.js`, lines 159-162 **Vulnerability Type**: T05: Unauthorized Access and Privilege Escalation **Risk Level**: Medium ### Evidence ```yaml tools: - filesystem - message.send # Retained as the official push channel # Added OpenClaw built-in cron trigger triggers: - type: cron expression: "0 * * * *" handler: onCronTick ``` The implementation exports only `main` and does not define or export the declared cron handler: ```js // Initialization ensureFiles(); // Export only main module.exports = { main }; ``` ### Technical Analysis The implemented functionality only appends local note content through `fs.promises`. Nevertheless, the metadata requests `message.send`, which grants an outbound messaging capability not used anywhere in `index.js`. The metadata also registers a recurring cron trigger for `onCronTick`, but no such function exists or is exported. The declared messaging and scheduling capabilities are not documented in `SKILL.md`, whose permissions section lists only filesystem access. Requesting permissions and execution mechanisms beyond those required by the actual implementation violates least-privilege principles. Even when currently unused, unnecessarily granted capabilities expand the consequences of future code changes, dependency compromise, or runtime misconfiguration. The invalid scheduled handler may also produce repeated errors. ### Attack Path 1. An operator installs the Skill based on its documented local note-writing behavior. 2. The runtime processes the metadata and grants both filesystem and outbound messaging access. 3. The runtime may register the recurring cron trigger despite the handler being absent. 4. If the Skill code is subsequently modified or another flaw enables code execution in its context, the unnecessarily granted messaging authority can be used to send data or messages outside the note repository. 5. The recurr ...[truncated 704 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `message.send` unless outbound notification functionality is implemented and explicitly required. - Remove the cron trigger unless the Skill has a documented recurring task. - If scheduled behavior is required, implement and export `onCronTick`, restrict its operations, and document its frequency and purpose. - Ensure `SKILL.md` accurately lists every requested tool and execution mechanism. - Add installation-time validation that rejects metadata referencing nonexistent handlers. - Apply capability-based authorization so each exported function receives only the tools it needs. - Require explicit operator consent for outbound messaging and recurring execution. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:7
Finding
Module Import Causes Unsolicited Writes to a Hard-Coded User Directory<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 7-13, 21-30, and 159 **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: Medium ### Evidence ```js const BASE_DIR = '/Users/macbook/Documents/OC_club'; const PATHS = { workTodo: path.join(BASE_DIR, '工作待办.md'), lifeTodo: path.join(BASE_DIR, '生活待办.md'), workRecord: path.join(BASE_DIR, '工作记录.md'), inspiration: path.join(BASE_DIR, '灵感.md') }; ``` ```js async function ensureFiles() { await fs.mkdir(BASE_DIR, { recursive: true }); for (const key of Object.keys(PATHS)) { try { await fs.access(PATHS[key]); } catch { await fs.writeFile(PATHS[key], ''); } } } ``` ```js // Initialization ensureFiles(); ``` ### Technical Analysis The module invokes `ensureFiles()` at import time. Consequently, loading the Skill for discovery, inspection, or registration immediately attempts to create a fixed absolute directory and four files, even if the user never invokes the Skill. The directory belongs to a specifically named macOS home path and is not derived from an approved vault configuration or the current user's context. There is no validation that it points to the intended Obsidian vault. The top-level promise is also not awaited or handled, potentially producing an unhandled rejection when directory creation fails. This behavior violates the principle that module loading should be free of unsolicited side effects. ### Attack Path 1. The agent host imports `index.js` while discovering or registering the Skill. 2. The top-level `ensureFiles()` call executes without a user invocation. 3. The process attempts to create `/Users/macbook/Documents/OC_club`. 4. If permissions allow, four Markdown files are created in that location. 5. Later invocations append user content to those fixed files, regardless of the active user's intended vault. 6. If permissions do not allow access, the unhandled asynchronous failure may affect process stability or ...[truncated 635 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the top-level `ensureFiles()` invocation. - Initialize storage only during an explicit, authorized Skill invocation or setup operation. - Require the vault path through trusted configuration rather than embedding a developer-specific absolute path. - Resolve the configured path with `path.resolve()` and verify that it remains inside an operator-approved root directory. - Confirm the target directory with the user or administrator before creating files. - Use restrictive file permissions where supported. - Await and handle all initialization errors. - Consider opening files with flags that avoid unintended replacement during creation. - Add tests confirming that importing the module causes no filesystem changes. ]]>

T08 · Insecure Dependencies

Warning
Location
smart-auto-note.html:6
Finding
Bundled HTML Executes a Remote Third-Party Script Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `smart-auto-note.html`, line 6 and lines 231-246 **Vulnerability Type**: T08: Insecure Dependencies **Risk Level**: Medium ### Evidence ```html <script src="https://cdn.jsdelivr.net/npm/mermaid@11.4.1/dist/mermaid.min.js"></script> ``` The remotely loaded library is subsequently initialized: ```html <script> mermaid.initialize({ startOnLoad: true, theme: 'dark', themeVariables: { background: '#0f141f', primaryColor: '#2563eb', primaryBorderColor: '#3b82f6', primaryTextColor: '#f1f5f9', lineColor: '#6b7c93' }, flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'basis' }, securityLevel: 'loose', fontFamily: '"Segoe UI", "Roboto", monospace' }); </script> ``` The same page describes the project as offline and free of third-party dependencies, which is inconsistent with the external CDN request. ### Technical Analysis Opening the HTML page causes the browser to download and execute JavaScript from jsDelivr. Although the Mermaid version is pinned, the script element does not contain a Subresource Integrity hash. The browser therefore has no cryptographic mechanism to verify that the retrieved content matches the artifact reviewed by the Skill auditor. This creates a supply-chain dependency on the CDN, package publication infrastructure, DNS resolution, TLS trust chain, and network path. It also causes an external request that discloses network metadata and contradicts the page's offline-operation claim. The `securityLevel: 'loose'` option further reduces Mermaid's output restrictions. No attacker-controlled Mermaid source was identified in this static file, so that option is not independently confirmed as exploitable in the reviewed project. ### Attack Path 1. A user opens `smart-auto-note.html` in a browser while connected to a network. 2. The browser requests Mermaid from ` ...[truncated 1027 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Bundle a reviewed Mermaid build within the project and reference it through a local relative path. - If a CDN is unavoidable, add a verified Subresource Integrity hash and `crossorigin="anonymous"`. - Define a restrictive Content Security Policy that permits scripts only from explicitly approved sources. - Reassess whether `securityLevel: 'loose'` is necessary; use Mermaid's stricter security mode where possible. - Document the dependency and external request accurately instead of claiming zero third-party dependencies or fully offline behavior. - Establish a controlled dependency update and review process. - Test the page with network access disabled to ensure the intended offline behavior remains available. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
该技能描述与代码存在明显不一致。首先,“语义智能识别自动分类”暗示较强的语义分析能力,但代码中的 classify() 和 classifyArchive() 仅依赖硬编码关键词包含判断,不属于通常意义上的语义智能识别。其次,描述称写入 Obsidian 笔记,但代码只是向 /Users/macbook/Documents/OC_club 下的若干 Markdown 文件追加内容,没有任何 Obsidian 插件、API、vault 交互或应用集成逻辑,因此更准确地说是写本地 Markdown 文件,而不是明确的 Obsidian 集成。最后,描述称“支持待办自动归档”,虽然代码有 doArchive()、pendingArchiveState 和归档格式化逻辑,但 main() 中并没有设置 pendingArchiveState 或检测待办完成并触发归档,因此这项能力在当前代码片段中并未真正实现。整体来看,技能的核心宣称比实际实现更强,且包含未接通的功能,应判定为描述与行为不匹配。

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The natural-language content of the skill is entirely in Chinese and describes direct natural-language triggering, but nowhere offers the user a language/locale option or states that the skill is intentionally limited to a Chinese-speaking context. Under the policy, forcing a specific language without user opt-in is a locale-policy violation.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Natural-language strings throughout the skill, including categories, prompts, file names, and status messages, are fixed in Chinese with no mechanism for language selection. This imposes a specific language/locale on all users rather than offering a choice or documenting a justified locale restriction.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The implementation operates on fixed local Markdown paths under a specific filesystem directory rather than interacting with Obsidian as an application or vault abstraction. It also defines archive helpers and state, but the main flow never sets pendingArchiveState or invokes archive logic from todo completion, so the claimed '待办自动归档' capability is not actually implemented.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill automatically creates and appends to user files in a hard-coded personal directory without an explicit first-run consent prompt or clear disclosure at execution time. In an agent setting, silent file modification can lead to unintended persistence, privacy issues, and unauthorized alteration of personal notes, especially when ordinary chat text is treated as writeable content.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The page declares `lang="zh-CN"`, and the visible UI text throughout the document is exclusively Chinese. Under the policy, forcing a specific language without user opt-in or a documented justification is a natural-language policy violation.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The page explicitly claims 'zero third-party dependencies' and 'no external interfaces' while importing Mermaid from a remote CDN, so the documentation is materially misleading. That remote script creates a supply-chain and network dependency: if the CDN, package, or transit path is compromised, arbitrary JavaScript can run in the page context.

Intent-Code Divergence

Low
Confidence
84% confidence
Finding
The comment '主入口 (纯净版)' and nearby notes suggest a simplified but coherent feature set, yet the archive confirmation state machine is still present without any code path that populates pendingArchiveState. This creates misleading documentation about effective behavior because the code advertises archive handling in comments and structure but cannot perform it in practice.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The manifest includes natural-language comments exclusively in Chinese such as '保留这个,这是官方推送通道' and '新增:OpenClaw 内置 Cron 定时触发器', which impose a specific language context without offering any language choice or documenting a region-specific need. This matches the policy category for language or locale constraints appearing in natural-language content.

Context-Inappropriate Capability

Low
Confidence
93% confidence
Finding
The HTML file loads executable JavaScript from a third-party CDN even though its purpose is only to render a static rules diagram. While this may be for convenience, any remote script execution expands the attack surface and could be abused through CDN compromise, dependency hijacking, or unexpected script changes.

Static analysis

No suspicious patterns detected.