Back to skill

Security audit

Obsidian Cleaner

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it can move Obsidian notes and other local files beyond the clearly documented attachment cleanup scope.

Review carefully before installing. Only run it on a backed-up vault, use --dry-run first, avoid broad trigger-based execution, and do not pass an absolute or parent-traversal attachments path. The publisher should remove .md and .txt from default cleanup or require explicit opt-in, and should confine the destination inside the selected vault.

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

Warning
Location
obsidian_cleaner.py:16
Finding

Undocumented Classification and Movement of Obsidian Notes as Attachments

Content
View full analysis

Vulnerability Details

File Location: obsidian_cleaner.py:16
Vulnerability Type: Excessive file-type scope leading to unintended file relocation
Risk Level: Medium

Vulnerable Code

python
SUPPORTED_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.webp', '.pdf', '.docx', '.doc', '.txt', '.md'}

The documented auto-discovery behavior in SKILL.md lists PNG, JPG, GIF, JPEG, WEBP, PDF, and DOCX files. The implementation additionally classifies .doc, .txt, and .md files as attachments.

Markdown files are the primary note format used by Obsidian. Consequently, a normal cleanup operation can move root-level notes into the attachment directory even though the user only requested attachment cleanup. The operation is performed automatically unless the user explicitly selects dry-run mode.

Technical Analysis

The file allowlist violates the principle of least surprise and grants the cleanup operation a broader scope than its documented purpose. File extensions alone are used to determine whether a root-level file should be moved, without distinguishing notes from attachments or requesting confirmation for potentially sensitive document types.

This is particularly unsafe for .md files because changing their location can alter Obsidian path resolution, break external automation, and invalidate links that depend on the note's original location. Although the operation moves rather than deletes files, it still modifies user data and vault structure.

Attack Path

  1. A vault contains one or more root-level Markdown or text notes.
  2. The user invokes the documented attachment cleanup command without --dry-run.
  3. find_loose_attachments() treats .md and .txt files as supported attachments.
  4. The files are passed to the move operation.
  5. The notes are relocated from the vault root into the configured attachment directory.
  6. Links, scripts, integrations, or workflows that expect the orig ...[truncated 634 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove note and text formats from the default attachment allowlist:

    python
    SUPPORTED_EXTENSIONS = {
        '.png', '.jpg', '.jpeg', '.gif', '.webp', '.pdf', '.docx'
    }
    
  2. If .md, .txt, or .doc support is required, place those extensions behind an explicit command-line option such as --include-documents.

  3. Require interactive confirmation before moving note formats, unless a separate non-interactive confirmation flag is supplied.

  4. Make dry-run the default behavior and require an explicit --apply option to modify the vault.

  5. Keep the documented extension list synchronized with the implementation.

  6. Add tests verifying that root-level Markdown notes are not moved by the default cleanup operation.

T09 · Insecure Skill Coding Practices

Warning
Location
obsidian_cleaner.py:41
Finding

Attachment Destination Path Is Not Confined to the Selected Vault

Content
View full analysis

Vulnerability Details

File Location: obsidian_cleaner.py:41-44
Vulnerability Type: Unvalidated path traversal and unrestricted filesystem destination
Risk Level: Medium

Vulnerable Code

python
def ensure_attachments_folder(vault_path: str, attachments_folder: str) -> str:
    """Ensure the attachments folder exists and return its full path."""
    attachments_path = os.path.join(vault_path, attachments_folder)
    os.makedirs(attachments_path, exist_ok=True)
    return attachments_path

The attachments_folder value is accepted from the --attachments command-line argument and joined to the vault path without validation or canonical containment checks.

If attachments_folder is absolute, os.path.join() discards the vault prefix. If it contains parent traversal components such as ../, the resulting path can resolve outside the vault. The code then creates that directory and moves matching vault files into it.

Technical Analysis

The implementation assumes that attachments_folder is a simple directory name, but this assumption is not enforced. Filesystem paths must be canonicalized and checked against an approved root before directory creation or file movement.

Examples of unsafe values include:

text
--attachments /tmp/exported-files
--attachments ../../outside-vault

In the first case, an absolute path becomes the destination directly. In the second case, traversal components escape the selected vault after path resolution. An existing attachment-path symlink may similarly redirect operations outside the intended vault because the resolved destination is not checked.

The vulnerability does not bypass operating-system permissions. However, it allows the operation to create directories and relocate files anywhere writable by the invoking user, contrary to the documented behavior of organizing files inside the vault.

Attack Path

  1. An attacker, unsafe wrapper, ...[truncated 1272 chars]
Remediation
View remediation

Remediation Suggestions

  1. Reject absolute attachment paths and permit only a relative directory beneath the vault.
  2. Resolve the vault and destination paths before performing any filesystem modification.
  3. Verify that the resolved destination is a strict descendant of the resolved vault.
  4. Reject traversal components and prevent symlink-based escape.
  5. Perform the same containment validation immediately before every move to reduce time-of-check/time-of-use risk.

Example hardening logic:

python
def ensure_attachments_folder(vault_path: str, attachments_folder: str) -> str:
    vault = Path(vault_path).expanduser().resolve()
    relative = Path(attachments_folder)

    if relative.is_absolute() or '..' in relative.parts:
        raise ValueError("Attachments folder must be a relative path inside the vault")

    destination = (vault / relative).resolve()

    if destination == vault or vault not in destination.parents:
        raise ValueError("Attachments folder must be contained within the vault")

    destination.mkdir(parents=True, exist_ok=True)

    if destination.is_symlink() or vault not in destination.resolve().parents:
        raise ValueError("Attachments folder resolves outside the vault")

    return str(destination)

Also add automated tests for absolute paths, ../ traversal, nested valid paths, and symlinked destination directories.

Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

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

md
## Notes

- Only moves files, doesn't delete anything
- Won't overwrite existing files (skips with warning)
- Case-insensitive file extension matching
- Safe to run multiple times

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The description presents attachment cleanup as harmless automation but does not warn that moving loose images and attachments can break Obsidian links, disrupt workflows, or relocate files the user did not intend to modify. Because the skill operates on a vault root and performs potentially destructive reorganization, the missing warning materially increases the risk of user data disruption.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill describes file-system behavior and explicitly instructs running a local Python script against a vault path, but it declares no tool scope or permissions. That mismatch makes the skill harder to govern safely and may allow an agent to read from arbitrary local paths without clear user-visible constraints.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger phrase "clean attachments" is broad and could match ordinary conversation rather than a deliberate request to reorganize files. Because the skill performs file-moving actions, accidental activation could cause unintended changes to a user's vault contents.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The metadata describes automatic cleanup of Obsidian vault attachments without defining scope limits, exclusions, or confirmation requirements. In a note-taking vault, broad file-moving behavior can unexpectedly alter user data organization, break links, or relocate files the user did not intend to touch, especially if activation is trigger-based and not clearly bounded.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description advertises automatic cleanup of files in an Obsidian vault without stating clear activation limits, preview behavior, or user confirmation requirements. For a file-moving skill, broad trigger language increases the chance of unintended invocation and filesystem changes, especially because the action targets user content and attachments.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The invocation guidance lists positive triggers but does not define boundaries for when the skill must not activate, such as general discussion of cleaning, attachments, or Obsidian workflows. This increases the chance of unintended invocation, especially because the skill can modify local files.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
73% confidence
Finding

The description implies the skill will perform cleanup actions in an Obsidian environment automatically, without indicating user choice over the affected scope. Even if intended as a convenience feature, insufficient opt-in can lead to unwanted file operations in the user's vault and increases the chance of accidental data disruption.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.