Back to skill

Security audit

Junyi Vault Organizer

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to be a legitimate Obsidian organizer, but its bundled writer has under-scoped filesystem write behavior that deserves review before installation.

Review this before installing. Use it only if you are comfortable granting local file write access for an Obsidian vault, and avoid invoking chunk_writer.py directly unless it is fixed to require a vault root and reject extensionless/out-of-vault paths. A safer version should preview the exact target path and content before writing, require explicit confirmation for ambiguous saves, and use atomic writes to avoid accidental data loss.

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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/chunk_writer.py:22
Finding

Unrestricted Filesystem Write and Extension Allowlist Bypass

Content
View full analysis
str: """Resolve file_path and ensure it stays within root. Rejects symlink escapes.""" resolved = os.path.realpath(os.path.expanduser(file_path)) if root: root_resolved = os.path.realpath(os.path.expanduser(root)) if not resolved.startswith(root_resolved + os.sep) and resolved != root_resolved: raise ValueError( f"Path escapes vault boundary: '{file_path}' resolves to '{resolved}', " f"which is outside '{root_resolved}'" ) ext = os.path.splitext(resolved)[1].lower() if ext and ext not in ALLOWED_EXTENSIONS: raise ValueError(f"File extension '{ext}' not in allowlist: {ALLOWED_EXTENSIONS}") return resolved def chunk_write(file_path: str, content: str, vault_root: str | None = None) -> dict: """Write content to file in chunks, breaking at paragraph boundaries. If vault_root is provided, the resolved file_path must stay within it. """ safe_path = safe_resolve(file_path, vault_root) ``` The command-line entry point does not provide a vault root: ```python def main(): if len(sys.argv) < 3: print("Usage: chunk_writer.py ") sys.exit(1) file_path = sys.argv[1] content_source = sys.argv[2] if content_source == '-': content = sys.stdin.read() else: with open(content_source, 'r', encoding='utf-8') as f: content = f.read() result = chunk_write(file_path, content) ``` ### Technical Analysis The filesystem containment check in `safe_resolve()` is conditional on the optional `root` argument. The CLI invokes `chunk_write(file_path, content)` without suppl ...[truncated 2262 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/chunk_writer.py:51
Finding

Empty Content Causes Destination Truncation Before Failure

Content
View full analysis
pos: end = newline_pos + 1 chunks.append(content[pos:end]) pos = end with open(safe_path, 'w', encoding='utf-8') as f: f.write(chunks[0]) for chunk in chunks[1:]: with open(safe_path, 'a', encoding='utf-8') as f: f.write(chunk) ``` ### Technical Analysis When `content` is empty, `total_chars` is zero and the chunk-building loop never executes. The `chunks` list therefore remains empty. The destination is then opened with mode `w`, which immediately creates or truncates the file. Evaluation of `chunks[0]` raises `IndexError` because the list is empty, but truncation has already occurred. The caller receives a failure even though an existing destination may already have been irreversibly emptied. The non-atomic writing design also means later write failures can leave partially written content, although the confirmed deterministic case is empty input. ### Attack Path 1. An attacker or malformed workflow invokes `chunk_write()` with an existing destination and an empty content string. 2. The chunk-building loop produces no chunks. 3. The script opens the destination in write mode, truncating it to zero bytes. 4. Access to `chunks[0]` raises `IndexError`. 5. The operation reports failure, but the original content has already been destroyed. Exploitation requires influence over the destination and supplied content. ### Impact Assessment The issue can cause loss of an existing note or another writable file. ...[truncated 243 chars]
Remediation
View remediation
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • 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
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a full auto-archiving knowledge-management skill with semantic triggers, domain-based organization, and Obsidian storage conventions. The supplied code only provides a low-level helper to write text to a file safely and in chunks. While file writing could support an archiving system, this chunk alone does not perform the core declared behavior: it does not detect user intent, process or organize notes, choose destination structures, or implement the described business/life-learning taxonomy. Therefore the actual behavior is materially narrower and different from the declared purpose.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The trigger conditions are extremely broad, including common phrases like 'save this', 'remember this', and generic requests to organize information. In an agent ecosystem, that can cause unintended activation and silent persistence of sensitive conversations, personal data, or unrelated content to disk without sufficiently specific user intent.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

The manifest metadata uses a highly expansive description and trigger surface aimed at capturing many forms of user content and persisting them to storage. In this context, the poisoning signal is credible because broad metadata can manipulate agent routing and tool selection, causing the skill to intercept ordinary conversations and invoke file-writing behavior more often than users expect.

Content

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

md
---
name: junyi-vault-organizer
version: 2.0.0
description: "自动归档 — A two-domain knowledge management system: Business domain (事业域, customizable name, organized by function) and Life & Learning domain (生活学习域, three-layer Inbox→Cognition→Guidebook). Stores to Obsidian vault. Triggers when user mentions 自动归档, 归档, 享育心塾萃取器, 萃取器, or asks to save/store/organize any information — articles, reading notes, class notes, children's words, conversations, ideas, reflections, quotes, or any content they want to keep. Also triggers on save this, remember this, file this away, put this in my

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill clearly instructs reading and writing files in a user's Obsidian vault, but it does not declare any explicit tool scope or permission boundaries. That creates a least-privilege failure: an agent may gain filesystem capabilities without user-visible constraints, increasing the chance of unintended or overbroad file access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill is designed to perform persistent filesystem writes to an Obsidian vault, but it does not provide an explicit user warning at the point of action that local files will be modified. This raises consent and data-handling risk because users may think they are merely taking ephemeral notes when the action actually creates durable artifacts on disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The file states that only Chinese directory names are used and that English aliases are not used, which imposes a specific language/locale convention. Because the document does not present this as an opt-in choice or clearly justify it as a region-specific requirement, it conflicts with the policy against forcing a language without user choice.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.