Back to skill

Security audit

Openclaw Obsidian

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Obsidian vault helper, but its optional shell hooks can run arbitrary local commands during ordinary vault operations, so it belongs in Review before installation.

Install only if you are comfortable granting the skill read/write authority over the configured vault. Leave OBSIDIAN_PRE_COMMAND and OBSIDIAN_POST_COMMAND unset unless you have reviewed the exact hook script yourself; if enabled, those hooks run with your user permissions even for reads and previews. Point OBSIDIAN_VAULT_RULES only at a dedicated non-sensitive Markdown rules file.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (22)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

text
__pycache__/
*.py[cod]
.venv/
.env

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as a file-first vault editor, but it explicitly supports execution of trusted shell hooks via OBSIDIAN_PRE_COMMAND and OBSIDIAN_POST_COMMAND and relies on external configuration from OBSIDIAN_VAULT_RULES. This expands the attack surface from note manipulation to arbitrary command execution, and the mismatch can mislead operators into granting or using the skill under a lower-risk assumption than its real behavior warrants.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill exposes pre/post command hooks that can execute arbitrary shell commands via environment variables before and after ordinary vault operations. That behavior materially expands the tool from file editing into command execution, which is especially dangerous because the advertised purpose is safe, file-first Obsidian vault manipulation.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This duplicate finding points to the same dangerous primitive: Popen(command, shell=True) with untrusted input. In the context of an agent skill advertised for safe vault editing, embedding a shell-execution path is an unjustified and high-risk expansion of capability.

Content

Scanner excerpt · scripts/vault.py (reported line 168)May include surrounding context.

python
raise ValueError()
    except ValueError as exc:
        raise VaultError('OBSIDIAN_HOOK_TIMEOUT must be a positive finite number') from exc
    process = subprocess.Popen(command, shell=True, cwd=vault.root, start_new_session=True,
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        _, stderr = process.communicate(timeout=timeout)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This duplicate finding points to the same dangerous primitive: Popen(command, shell=True) with untrusted input. In the context of an agent skill advertised for safe vault editing, embedding a shell-execution path is an unjustified and high-risk expansion of capability.

Content

Scanner excerpt · scripts/vault.py (reported line 168)May include surrounding context.

python
raise ValueError()
    except ValueError as exc:
        raise VaultError('OBSIDIAN_HOOK_TIMEOUT must be a positive finite number') from exc
    process = subprocess.Popen(command, shell=True, cwd=vault.root, start_new_session=True,
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        _, stderr = process.communicate(timeout=timeout)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/test_capabilities.py (reported line 22)May include surrounding context.

python
self.base = Path(self.temp.name)
        self.root = self.base / 'vault'
        self.root.mkdir()
        self.env = {k: v for k, v in os.environ.items() if not k.startswith('OBSIDIAN_')}
        self.env.update(OBSIDIAN_VAULT_PATH=str(self.root), XDG_CACHE_HOME=str(self.base / 'cache'))

    def note(self, name, content):

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/test_vault.py (reported line 21)May include surrounding context.

python
self.base = Path(self.temp.name)
        self.root = self.base / 'vault'
        self.root.mkdir()
        self.env = {k: v for k, v in os.environ.items() if not k.startswith('OBSIDIAN_')}
        self.env.update(OBSIDIAN_VAULT_PATH=str(self.root), XDG_CACHE_HOME=str(self.base / 'cache'))

    def note(self, name, content):

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · README.md (reported line 10)May include surrounding context.

Install

sh
mkdir -p ~/.openclaw/skills
git clone https://github.com/lanjelin/openclaw-obsidian.git \
  ~/.openclaw/skills/openclaw-obsidian

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · README.md (reported line 42)May include surrounding context.

md
Ask your agent, for example:

- “Find my notes about the garden project.”
- “Create a note in Inbox with these meeting notes.”
- “Show backlinks to Projects/Website.md.”
- “Preview renaming this note and updating its links.”
- “List empty project folders without removing them.”

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · README.md (reported line 97)May include surrounding context.

--workdir /home/you/.local/state/obsidian-bisync

text

Run `chmod 700 /home/you/.local/bin/obsidian-bisync`, then add to your OpenClaw `.env`:

```dotenv
OBSIDIAN_PRE_COMMAND="/home/you/.local/bin/obsidian-bisync"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill exposes broad effective capabilities (environment access, file read/write, shell execution, and possibly network via helper behavior) without declaring an explicit tool scope or permissions boundary. That makes the operational trust model unclear and increases the chance an agent will invoke high-risk actions that users or the platform did not expect.

Content

No source excerpt is available for this finding.

Context Window Stuffing

Medium
Category
Memory Poisoning
Confidence
90% confidence
Finding

Skill attempts to fill the context window with filler content, displacing legitimate instructions and safety constraints. This can degrade agent performance or bypass safety boundaries.

Content

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

md
### Help generate a rules file

When requested, use `examples/vault-rules.md` as a starting point. Gather the user's protected/no-read folders, default note destination, naming, templates, and directory-specific conventions; do not invent private folder names or permissions. With no configured rules, use conservative defaults for any authorized discovery. Propose the resulting Markdown and save it at a user-approved absolute path outside the vault using a file-writing tool. Never overwrite existing rules implicitly. Explain how to set `OBSIDIAN_VAULT_RULES`; do not change environment or executable hooks without authorization.

## Discover and read

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · examples/vault-rules.md (reported line 16)May include surrounding context.

md
- When creating a requested note, create its missing allowed destination folder with the helper's `mkdir` command (`--parents` for nested folders). This includes the default `Inbox/`; do not leave it as a future destination merely because it does not exist. Directory creation obeys the same no-read/no-write rules and never initializes a missing vault root.
- Discover directories with `list --type dirs|all --under DIR`; use `--empty` only with `--type dirs`. Empty-folder discovery is not cleanup authorization, even when folders appear unused. Hidden/protected/symlink children still make a directory nonempty.
- Scope requested discovery with `search QUERY --under DIR` or `links [PATH] --under DIR`. Scope restricts source-note reads, not target resolution: outside-scope links still resolve using the full allowed inventory. Backlinks and move/delete/anchor refactors remain broad; scope never overrides no-read restrictions.
- Remove empty folders only when the user requests removal or explicitly authorizes cleanup covering those paths. Use the helper's `rmdir` preview followed by `--apply`; never remove contents, recurse, or automatically delete empty parents after note operations. Retain all directory restrictions.
- Prefer unambiguous vault-relative wiki links, for example `[[Projects/Example]]`. Preserve an existing link style when editing a note; do not rewrite external URLs or code examples.
- Keep attachments in `Attachments/` and preserve existing relative references. Only import explicitly user-specified local files with `import-attachment SOURCE DEST`: SOURCE is an absolute regular local file without symlink path components; DEST is an allowed non-`.md` path with an existing parent. Never overwrite, execute contents, or download automatically. In-vault sources retain their read restrictions. Do not replace, rename, or delete attachments unless explicitly requested and supported; never substitute shell mutations.
- Use literal `append PATH --content-file FILE --e
...[truncated 25 chars]

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The rules file path is taken from an absolute environment variable and later rules_data() returns its full content to the caller. That permits exfiltration of arbitrary readable local files outside the vault if an attacker can point OBSIDIAN_VAULT_RULES at a sensitive path, which exceeds the stated vault-scoped purpose of the skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Hook execution occurs implicitly during normal command handling with no explicit prompt-time disclosure at the execution site. In practice this means a seemingly benign vault read or write may also run arbitrary local commands, undermining user expectations and increasing the chance of stealthy abuse.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

The hook() function reads a command string from environment variables and executes it with subprocess.Popen(..., shell=True). Any actor who can influence the environment for this tool can trigger arbitrary shell execution in the context of the agent, and shell=True further enables shell metacharacter injection and command chaining.

Content

Scanner excerpt · scripts/vault.py (reported line 168)May include surrounding context.

python
raise ValueError()
    except ValueError as exc:
        raise VaultError('OBSIDIAN_HOOK_TIMEOUT must be a positive finite number') from exc
    process = subprocess.Popen(command, shell=True, cwd=vault.root, start_new_session=True,
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        _, stderr = process.communicate(timeout=timeout)

Tainted flow: 'command' from os.environ.get (line 159, credential/environment) → subprocess.Popen (code execution)

Medium
Category
Data Flow
Confidence
99% confidence
Finding

This is a real tainted-data-to-code-execution flow: command comes directly from os.environ.get and is passed to subprocess.Popen for execution. In an agent skill context, environment variables are configuration inputs rather than trusted code, so this creates an arbitrary command execution primitive unrelated to core vault file editing.

Content

Scanner excerpt · scripts/vault.py (reported line 168)May include surrounding context.

python
raise ValueError()
    except ValueError as exc:
        raise VaultError('OBSIDIAN_HOOK_TIMEOUT must be a positive finite number') from exc
    process = subprocess.Popen(command, shell=True, cwd=vault.root, start_new_session=True,
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        _, stderr = process.communicate(timeout=timeout)

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 · tests/test_capabilities.py (reported line 38)May include surrounding context.

python
return str(path)

    def cli(self, *args, code=0):
        result = subprocess.run([sys.executable, str(HELPER), *args], env=self.env,
                                capture_output=True, text=True, timeout=15)
        self.assertEqual(result.returncode, code, result.stdout + result.stderr)
        return json.loads(result.stdout)

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 · tests/test_vault.py (reported line 31)May include surrounding context.

python
return hashlib.sha256(path.read_bytes()).hexdigest()

    def cli(self, *args, code=0):
        result = subprocess.run([sys.executable, str(HELPER), *args], env=self.env,
                                capture_output=True, text=True, timeout=10)
        self.assertEqual(result.returncode, code, result.stdout + result.stderr)
        return json.loads(result.stdout)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The import-attachment command reads an external file and writes it into the vault with atomic_write, which affects user data. This function contains no confirmation prompt, print/log statement, or warning comment/docstring describing that it copies external content into the vault.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The helper methods file() and input() perform file writes, which fall under the code-file warning criteria for potentially data-affecting operations. Although this is test code, these functions have no docstring or comment explaining that they create or overwrite files in a temporary test workspace.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The test helper invokes an external Python script via subprocess.run, which is a safety-relevant operation under the code-file warning criteria. While the module docstring describes CLI coverage generally, there is no comment, docstring, print, or other explicit disclosure near this helper that it executes a subprocess.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.