Back to skill

Security audit

bullet-journal-gen

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it saves personal/work notes into persistent HTML and backup files with unsafe rendering and weak file-path controls.

Install only if you are comfortable with the skill writing journal content, work notes, generated HTML, printable files, JSON data, and backups to local disk. Avoid processing note text from untrusted sources until HTML escaping is fixed, and treat the interactive/local-server workflow as incomplete unless you review and constrain the server implementation yourself.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_card.py:48
Finding

Persistent HTML and JavaScript Injection in Generated Journal Cards

Content
View full analysis
List[Dict]: lines = raw_input.strip().split('\n') for line in lines: line = line.strip() if not line: continue time_str, has_time, content = self.extract_time(line) polished_content = self.polish_language(content) symbol, note_type, _ = self.classify_note( polished_content, has_time ) is_work_study = self.is_work_study(polished_content) note = { 'symbol': symbol, 'type': note_type, 'content': polished_content, 'time': time_str, 'has_time': has_time, 'is_work_study': is_work_study, 'raw': line } self.processed_notes.append(note) ``` The standard card generator directly interpolates that content into HTML: ```python # scripts/generate_card.py:48-75 def generate_notes_html(self, notes: List[Dict]) -> str: """Generate note content as HTML.""" html_lines = [] for note in notes: symbol_class = note['type'] time_html = "" if note.get('time'): time_html = f'{note["time"]}' note_html = f"""
{note['symbol']}
{time_html}{note['content']}
""" html_lines.append(note_html) ...[truncated 5721 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/task_manager.py:94
Finding

Path Traversal in Task-State File Operations

Content
View full analysis
Dict: task_file = os.path.join(self.tasks_dir, date, 'tasks.json') os.makedirs(os.path.dirname(task_file), exist_ok=True) if os.path.exists(task_file): with open(task_file, 'r', encoding='utf-8') as f: tasks_data = json.load(f) else: tasks_data = {'date': date, 'tasks': []} existing_task = next( (t for t in tasks_data['tasks'] if t['id'] == task_id), None ) ``` Status updates construct the path in the same way: ```python # scripts/task_manager.py:177-190 task_file = os.path.join(self.tasks_dir, date, 'tasks.json') if not os.path.exists(task_file): raise FileNotFoundError(f"Task file does not exist: {task_file}") with open(task_file, 'r', encoding='utf-8') as f: data = json.load(f) ``` Task migration also accepts an unvalidated destination date: ```python # scripts/task_manager.py:252-275 new_task_file = os.path.join( self.tasks_dir, new_date, 'tasks.json' ) os.makedirs(os.path.dirname(new_task_file), exist_ok=True) if os.path.exists(new_task_file): with open(new_task_file, 'r', encoding='utf-8') as f: new_data = json.load(f) else: new_data = {'date': new_date, 'tasks': []} new_task = task.copy() new_task['status'] = 'task' new_task['status_history'] = [{ 'from': 'migrated', 'to': 'task', 'timestamp': datetime.now().isoformat(), 'notes': f'Migrated from {date}' }] new_ ...[truncated 3200 chars]
Remediation
View remediation
str: parsed = datetime.strptime(value, "%Y-%m-%d") normalized = parsed.strftime("%Y-%m-%d") if value != normalized: raise ValueError("Invalid date") return normalized ``` 2. Reject absolute paths, separators, null bytes, parent-directory components, and values that do not exactly match the canonical date format. 3. Resolve and verify every constructed path: ```python from pathlib import Path root = Path(self.tasks_dir).resolve() candidate = (root / validated_date / "tasks.json").resolve() if root not in candidate.parents: raise ValueError("Task path escapes storage root") ``` 4. Centralize path construction in one private helper and use it from `create_task`, `update_task_status`, `migrate_task`, and `get_daily_tasks`. Do not duplicate unchecked path construction. 5. If an HTTP API is added: - Validate request bodies against a strict schema. - Authenticate state-changing requests. - Apply CSRF protection where browser clients are used. - Bind only to an explicitly configured trusted interface. - Return generic errors rather than exposing full filesystem paths. 6. Use atomic writes to prevent corruption: - Write validated JSON to a temporary file inside the same directory. - Flush and synchronize it. - Replace the destination atomically. 7. Validate loaded JSON structure before accessing or modifying it. Confirm that `tasks` is a list and that each task has an allowed status and expected field types. 8. Run the task service under an account restricted to the project data directory so that path-validation failures cannot affect unrelated files. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (43)

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

该代码片段与“子弹笔记生成”这一核心方向基本相关,但实现范围明显小于声明。实际可见能力仅是:1)把日期、天气、温度与笔记条目拼接成标准化文本;2)依赖外部 NoteProcessor 对示例输入做解析。声明中的多项关键能力——尤其是可视化卡片、打印版本、自动合并到 Obsidian、标签/双链支持——在代码中完全没有体现,且这些并非单纯的底层实现细节,而是面向用户的重要功能承诺。因此应判定为描述与实际行为存在不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding

该代码片段的主要职责是“生成交互式 HTML 卡片”,而不是描述中宣称的完整“子弹笔记生成 skill”。它依赖外部 notes 输入,负责渲染任务、事件、笔记,附带天气/温度/统计,并保存为 HTML。虽然“生成可视化卡片”与描述部分一致,但描述中的多个核心能力——自然语言到规范子弹笔记的转换、润色、打印版本、Obsidian 日志合并、标签和双链支持——在此代码中都没有体现。代码还强调任务状态交互和任务管理数据读取,这属于描述未明确覆盖的实现重点。因此该描述与实际代码行为存在明显不完全匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

代码与描述有部分重合:确实生成了规范化的 Obsidian 日志,包含标签、双链、任务/事件/笔记分类,也体现了对工作学习内容的筛选流程(通过外部处理器示例调用)。但描述中的若干核心能力在该代码块中并未实现,尤其是自动润色、可视化卡片、打印版本,以及与现有日志的自动合并。此外,这段代码的核心是“从结构化 notes 生成 Obsidian Markdown”,而不是完整地把零散自然语言直接转换为子弹笔记。因此描述比实际实现更宽,存在明显能力宣称不符。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a note-processing and journaling skill whose core purpose is transforming natural language into structured bullet-journal entries and integrating them into Obsidian. However, this specific code chunk is focused on presentation: it renders provided note objects into a standalone HTML card with CSS/JS for interactive task status changes and summary statistics. Although visual card generation aligns partially with the description, the primary behaviors advertised—natural language conversion, automatic categorization pipeline, language polishing, Obsidian log merging, tags/backlinks, and print-version generation—are not implemented here. The undeclared interactive task-state manipulation is a notable extra behavior, though local and self-contained. Overall, the code materially underdelivers on the declared purpose and has a different primary role: standalone card rendering rather than note ingestion and Obsidian integration.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

声明描述的核心能力是笔记生成与日志整理,但代码中没有任何自然语言解析、笔记结构化、任务管理、Obsidian 集成、标签或双链处理逻辑。相反,代码定义了 WeatherFetcher,用于获取(目前为模拟)天气数据、映射天气图标、补充日期与温度单位,并在 main 中打印天气信息。这属于与声明用途明显不同的主要功能,因此构成高置信度不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

代码与描述的核心前半部分基本一致:它确实把零散输入转换为较规范的子弹笔记,进行简单分类和语言润色,并识别工作学习相关内容。但描述中若干重要能力在代码中没有体现,包括写入/合并到 Obsidian 日志、标签与双链生成、可视化卡片和打印版本输出。代码也没有任何文件写入、Obsidian 集成或渲染相关逻辑。因此描述明显高于代码实际能力,属于描述与行为不完全匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The declared description presents a fairly high-level note-generation and Obsidian-integration skill. The supplied code instead implements a local task management backend centered on bullet-journal task status transitions and JSON persistence. There is some thematic overlap: it uses bullet-journal symbols, handles tasks/events, and formats entries in a bullet-journal style. However, the core promised behaviors—transforming arbitrary natural language into structured notes, polishing text, generating printable/visual outputs, and syncing/merging into Obsidian with tags and backlinks—are absent. Conversely, the code’s primary purpose is task lifecycle/state management with filesystem storage, which is materially different from the declared generation-and-integration workflow. Therefore this is a meaningful description/behavior mismatch.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger phrases are extremely broad and overlap with ordinary conversation such as '记下来', '今天总结', and '待办事项'. In a skill that automatically saves files, creates backups, and integrates with Obsidian, broad triggers raise the chance of accidental invocation, causing unintended capture, transformation, and storage of sensitive user content without clear intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This is a markdown file, so SQP-2 applies to user-facing documentation. Lines L101-L105 state that every status change automatically updates local JSON files and regenerates the printable HTML, which affects user data on disk, but this behavior is presented as a feature rather than an explicit warning or caution about persistence and overwrite effects.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · INTERACTIVE_GUIDE.md (reported line 258)May include surrounding context.

解决方案:

bash
# 检查服务器是否运行
curl http://localhost:8000/health

# 查看浏览器控制台(F12)查看错误信息

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The overview describes Phase 2 as actively obtaining weather, temperature, humidity, and wind direction, and the project structure lists a weather script. However, the same document later lists '天气API集成' as a short-term future plan, indicating this capability is not actually implemented yet. This creates a manifest/documentation mismatch about what the skill currently does.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The overview explicitly states generated notes are automatically saved and backed up, but provides no warning about persistent writes of potentially sensitive personal and work content. In a journaling skill that processes daily activities, plans, and work notes, silent storage and backup increase privacy and data exposure risk, especially if users do not realize multiple copies will be created.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The entire skill description and usage guidance are presented only in Chinese, with no indication that users may choose another language or locale. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly documented and justified.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill documents capabilities that imply shell execution, file read/write, and network access, but it does not declare any tool scope or permissions boundaries. In a skill that automatically saves notes, fetches weather, and writes Obsidian logs/PDF/HTML, missing scope declarations can let the agent invoke broader tools than users expect, increasing the risk of unauthorized file modification, data exposure, or command execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill description does not clearly warn users that content may be automatically saved, backed up, and merged into Obsidian logs. Because the skill processes diary-like and work-related notes, lack of disclosure undermines informed consent and can lead to unexpected persistence of sensitive personal or professional information.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documented behavior expands into backup, versioning, summaries, statistics, and search beyond the manifest's stated scope, which increases the amount of data retained and processed. For a journaling skill handling potentially sensitive personal and work information, undocumented retention and indexing features can materially increase privacy and data exposure risk, especially when combined with automatic saving and backup.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The documentation says the system will automatically retrieve weather information, which implies a network call and possible transmission of metadata such as time, locale, or inferred location, but provides no privacy or network notice. In a personal journaling workflow, undisclosed external requests can expose contextual user data and violate user expectations about offline processing.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill states that generated content is automatically saved and backed up, but does not clearly warn users that their potentially sensitive journal text will be persisted to disk in multiple files and locations. For a journaling skill that may contain personal, work, or academic details, silent persistence increases the risk of unintended disclosure, over-retention, and storage in locations the user did not expect.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This markdown file is natural-language guidance, and all user-facing instructions are presented in Chinese from start to finish. The file does not indicate that Chinese is optional, nor does it explain that the skill is intentionally region- or language-specific, which can violate a language/locale policy requiring user choice or opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The module docstring and function docstrings specify that the script generates standardized bullet journal text in Chinese, and the implementation hard-codes Chinese weekday/weather labels. This imposes a specific language/locale behavior without offering user opt-in or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This Python file contains natural-language descriptions, docstrings, sample content, and user-facing print messages entirely in Chinese, indicating the skill forces a specific language experience. The policy requires offering a language/locale choice or clearly documenting a justified locale restriction, neither of which is present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This Python file contains its module docstring, class/method docstrings, comments, and user-facing print output in Chinese only. Under the policy, forcing a specific language without offering a language or locale choice is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file’s docstrings, comments, sample content, and printed user messages are all written in Chinese, which imposes a specific language on users without any visible option to select another locale. Under the policy, a forced language choice without opt-in is a natural-language policy violation unless clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest describes transforming natural-language notes into bullet-journal format, visual cards, and printable versions, but does not indicate any need to execute external programs. Calling wkhtmltopdf through subprocess.run introduces system-level execution capability beyond ordinary text/HTML generation.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_pdf.py (reported line 157)May include surrounding context.

python
f.write(html_content)

            try:
                subprocess.run([
                    'wkhtmltopdf',
                    '--page-size', 'A4',
                    '--margin-top', '0',

Static analysis

No suspicious patterns detected.