Back to skill

Security audit

NotebookLM Distiller

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent NotebookLM-to-Obsidian purpose, but its write path is under-scoped and can overwrite files outside the intended vault.

Review before installing. Use only with a dedicated test vault or tightly controlled vault path, avoid agent-autonomous persist/writeback use, inspect exact destination paths before writes, and pin notebooklm-py to a reviewed version. The main issue is not hidden malware, but unsafe write scoping and broad auto-execution instructions around durable local and NotebookLM changes.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/distill.py:541
Finding
Arbitrary File Overwrite Outside the Configured Obsidian Vault<![CDATA[ ## Vulnerability Details **File Location**: `scripts/distill.py:541-543` **Supporting Write Sink**: `scripts/distill.py:116-120` **Vulnerability Type**: Path traversal and unrestricted file overwrite **Risk Level**: High ### Vulnerable Code ```python def write_note(filepath: str, content: str) -> None: """Write content to filepath, creating parent dirs as needed.""" os.makedirs(os.path.dirname(filepath), exist_ok=True) with open(filepath, "w", encoding="utf-8") as f: f.write(content) logging.info(f"Saved → {filepath}") ``` ```python rel_path = args.path # ... out_path = os.path.join(vault_dir, rel_path) write_note(out_path, full_content) print(f"Persisted → {out_path}") ``` ### Technical Analysis The `persist` subcommand accepts `args.path` and joins it directly with the configured vault directory. The code does not reject absolute paths, normalize traversal components, resolve symbolic links, or verify that the resulting destination remains within the vault. Two bypass patterns are possible: 1. A traversal path such as `../../.bashrc` escapes the vault after filesystem path resolution. 2. An absolute `--path` causes `os.path.join(vault_dir, rel_path)` to discard `vault_dir` entirely. The resulting path is passed to `write_note`, which creates missing parent directories and opens the destination in `"w"` mode. This silently truncates and replaces an existing file. ### Attack Path 1. An attacker supplies or influences a request that causes the Agent to invoke the `persist` subcommand. 2. The attacker provides a path such as: ```text ../../home/user/.bashrc ``` or an absolute path such as: ```text /home/user/.config/example/config ``` 3. `os.path.join` constructs a destination that is outside the configured vault. 4. `write_note` creates parent directories where necessary. 5. The target file is opened in write mode and overwritten with attacker-influenced Markdown and frontmatter. 6. If the sel ...[truncated 1073 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Treat `--path` strictly as a vault-relative path and enforce containment before writing: 1. Reject empty and absolute paths using `os.path.isabs`. 2. Resolve the vault root and candidate destination with `os.path.realpath`. 3. Verify containment with `os.path.commonpath`. 4. Account for symbolic links by checking the resolved destination, not only the lexical path. 5. Reject destinations equal to the vault root or otherwise unsuitable as files. 6. Avoid unconditional overwrite. Use exclusive creation mode or require an explicit overwrite flag. 7. Add automated tests for `../`, nested traversal, absolute paths, and symlink escapes. Example hardening: ```python vault_root = os.path.realpath(os.path.expanduser(args.vault_dir)) if os.path.isabs(args.path): raise ValueError("--path must be relative to the vault") out_path = os.path.realpath(os.path.join(vault_root, args.path)) if os.path.commonpath([vault_root, out_path]) != vault_root: raise ValueError("--path resolves outside the configured vault") if os.path.isdir(out_path): raise ValueError("--path must identify a file") write_note(out_path, full_content) ``` For stronger overwrite protection, open new files with mode `"x"` and require a separately authorized option before replacing an existing note. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unpinned and Unverified Third-Party Dependency<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1` **Related Locations**: `SKILL.md:8-11`, `SKILL.md:65-68`, `README.md:35-38` **Vulnerability Type**: Mutable dependency and supply-chain exposure **Risk Level**: Medium ### Vulnerable Configuration ```text notebooklm-py>=0.3.0 ``` The Skill metadata also directs installation without a version or integrity constraint: ```yaml install: pip: ["notebooklm-py"] ``` The documented installation command is: ```bash pip3 install notebooklm-py ``` ### Technical Analysis The dependency specification allows any available version at or above `0.3.0`. No lockfile, package hash, or other integrity verification is provided. Consequently, installations performed at different times may execute materially different third-party code from the code reviewed with this Skill. This dependency is security-sensitive because the installed `notebooklm` CLI is executed by the Skill and interacts with an authenticated NotebookLM session. A compromised package release, compromised package index account, or unexpectedly unsafe future version could therefore execute with the privileges of the user running the Skill and access resources available to that CLI. This finding concerns unsafe dependency controls. The audited repository does not itself prove that the current `notebooklm-py` package is malicious. ### Attack Path 1. The Skill is installed according to its metadata or documentation. 2. The package manager resolves `notebooklm-py>=0.3.0` to a mutable future release rather than a specifically reviewed artifact. 3. A compromised or malicious release is downloaded from the configured package index. 4. Package installation behavior or subsequent `notebooklm` CLI execution runs the changed code with the user's privileges. 5. That code may access local files, the authenticated NotebookLM session, notebook data, and network resources available to the user process. Exploitation depends on compromise or mal ...[truncated 672 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the dependency to an exact, reviewed version: ```text notebooklm-py==<reviewed-version> ``` 2. Generate and commit a lockfile containing transitive dependency versions. 3. Use package hashes, for example through a hash-locked requirements file and: ```bash pip install --require-hashes -r requirements.txt ``` 4. Keep the Skill metadata and installation documentation consistent with the pinned version. 5. Install dependencies in an isolated virtual environment rather than the user's global Python environment. 6. Review release provenance, package ownership, and release notes before updating. 7. Re-run security review and functional tests for every dependency upgrade. 8. Where supported, use a trusted internal package mirror or artifact registry containing approved packages. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (22)

Vague Triggers

High
Confidence
96% confidence
Finding
The persist operation performs filesystem writes into an Obsidian vault, yet it is triggered by broad phrases such as 'persist this to vault' or 'write this into the knowledge base.' A write-capable action with loose activation criteria can cause unintended file creation or overwriting from ambiguous chat context, making this materially more dangerous than read-only operations.

Session Persistence

Medium
Category
Rogue Agent
Content
- **`quiz`** — Generate quiz questions as JSON for agent-orchestrated interactive sessions (e.g. Discord)
- **`evaluate`** — Evaluate a user's answer against notebook sources; returns structured feedback as JSON
- **`research`** — Start a NotebookLM web research session on any topic, wait for completion, output the notebook ID for follow-up distillation
- **`persist`** — Write any Markdown content directly into your Obsidian vault with frontmatter

No web-scraping dependencies required — pairs with [DeepReader](https://github.com/astonysh/OpenClaw-DeepReeder) for full URL-to-Obsidian automation.
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.

Session Persistence

Medium
Category
Rogue Agent
Content
### Subcommand: `research`

Create a new NotebookLM notebook from web research on any topic and wait for it to finish.

```bash
python3 ~/.openclaw/skills/notebooklm-distiller/scripts/distill.py research \
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.

Session Persistence

Medium
Category
Rogue Agent
Content
### Subcommand: `persist`

Write any Markdown content into your Obsidian vault with auto-generated YAML frontmatter.

```bash
# From inline content
Confidence
78% confidence
Finding
This capability explicitly allows writing arbitrary Markdown into a user-specified vault, creating durable local state from agent-supplied or model-generated content. In a tool-using agent environment, that persistence can be abused for note injection, misleading future context, or writing unreviewed content into synced locations if not strongly constrained.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The `persist` and related write behaviors are described functionally but without a prominent warning that they perform filesystem writes into a user-supplied vault path. In agent-integrated environments, underemphasizing write-side effects can lead to accidental overwrites, sensitive note injection, or persistence of untrusted content into a synced knowledge base.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The README states the skill handles only distillation, but elsewhere documents `research` and `persist`, including arbitrary Markdown writes into an Obsidian vault. This mismatch can mislead operators, policy engines, or users into granting broader trust or permissions than intended, increasing the chance of unsafe invocation or unnoticed file-writing behavior.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The natural-language triggers are broad and action-oriented, allowing an orchestrating agent to interpret casual requests as authorization to research topics or write content into a vault. In an agentic context, ambiguous triggers can cause unintended external actions, including notebook creation and filesystem writes, without a sufficiently explicit confirmation boundary.

Vague Triggers

Medium
Confidence
94% confidence
Finding
README 中给出的自然语言触发示例非常宽泛,例如“研究一下…蒸馏后存入知识库”“把这段对话的结论存到 Obsidian”等,容易与普通对话重叠。如果宿主 agent 依据这些示例进行模糊意图匹配,可能在用户未明确授权时触发 research、distill 或 persist,导致意外的外部请求、内容归档或写入本地知识库。

Session Persistence

Medium
Category
Rogue Agent
Content
# NotebookLM Distiller

Automated knowledge extraction pipeline: search NotebookLM notebooks by keyword → generate deep questions or structured summaries → write linked Obsidian markdown notes.

**Five subcommands:**
- `distill` — extract knowledge from existing notebooks (qa / summary / glossary)
Confidence
72% confidence
Finding
The skill is designed to persist generated content as linked Obsidian markdown notes, which creates durable local artifacts from notebook-derived data. This persistence increases security and privacy risk because sensitive or unreviewed material may be written to disk and retained beyond the immediate session.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The skill auto-triggers the research flow on broad natural-language phrases like 'research this topic' and explicitly instructs the agent to execute immediately without clarification. Because the skill has bash/read/write permissions and the research action creates new NotebookLM sessions and may lead to follow-on persistence, an ambiguous user message could cause unintended external actions and data creation.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The quiz/evaluate flow is activated by common phrases like 'quiz me on X' and '测验', which are easy to mention casually or in quoted text. Since this orchestration can repeatedly call external commands and drive a multi-step Discord interaction, broad matching increases the risk of accidental invocation and unintended disclosure of notebook-derived content.

Session Persistence

Medium
Category
Rogue Agent
Content
## Subcommand: persist

Write any markdown content into the Obsidian vault with auto-generated YAML frontmatter.

```bash
# From inline content
Confidence
93% confidence
Finding
The persist subcommand explicitly writes arbitrary markdown content into the Obsidian vault with generated frontmatter, enabling durable storage of whatever content is passed to it. In the context of broad activation criteria and write permissions, this persistence can preserve sensitive data, poisoned content, or misleading notes on the local filesystem.

Session Persistence

Medium
Category
Rogue Agent
Content
Subcommands:
  distill   Extract knowledge from NotebookLM notebooks into Obsidian markdown.
  research  Kick off a NotebookLM web research session on a topic.
  persist   Write any markdown content directly into the Obsidian vault.
"""

import argparse
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.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""Run a CLI command and return stdout. Returns '' on error/timeout."""
    logging.info(f"[RUN] {' '.join(cmd)}")
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
    except subprocess.TimeoutExpired:
        logging.error(f"[ERROR] Command timed out after {timeout}s: {' '.join(cmd)}")
        return ""
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
write_note writes arbitrary content to an arbitrary filepath and creates parent directories without any path restriction. In this skill's context, that is more dangerous because it is explicitly designed to modify an Obsidian vault, and if an attacker controls the target path through higher-level arguments they can overwrite files outside the intended vault via path traversal or absolute paths.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
writeback_to_notebook reports success even when the underlying command may have failed or produced no verifiable result. In a system that persists generated content back into a notebook, false success can hide failed writes, mislead downstream automation, and cause integrity and auditability problems around what was actually stored.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The persist command combines attacker-controlled --path with os.path.join(vault_dir, rel_path) and then writes directly, but it never normalizes or validates that the final path remains inside the vault. Because absolute paths override the base path and relative traversal segments like ../ can escape it, a user or calling agent could write arbitrary files anywhere the process has permission, not just into Obsidian.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The test file presents all user-facing instructions and validation criteria in Chinese, which effectively forces a specific language for operators. The policy allows locale constraints only when users are given a choice or the restriction is clearly documented and justified, neither of which appears here.

Unpinned Dependencies

Low
Category
Supply Chain
Content
notebooklm-py>=0.3.0
Confidence
94% confidence
Finding
The dependency is specified with a minimum version only (`>=0.3.0`), which allows future releases to be installed without review. This creates supply-chain risk because a later compromised or breaking release could be pulled into the environment unexpectedly, reducing build reproducibility and potentially introducing vulnerable code.

Intent-Code Divergence

Low
Confidence
89% confidence
Finding
The module-level help text presents the script as having distill, research, and persist subcommands, but the code also implements quiz and evaluate commands later in the file. This is intent-level documentation divergence because the user-facing documentation understates the actual exposed capabilities of the tool.

Missing User Warnings

Low
Confidence
83% confidence
Finding
Case 4 says it will '发起网络调研,新建 notebook' and provides the command to run, but there is no explicit warning about network activity or remote-side content creation. Because this behavior can affect privacy and external system state, the markdown description should surface that impact to the user.

Missing User Warnings

Low
Confidence
86% confidence
Finding
This markdown file instructs users to run commands that write notes directly into `/tmp/test-vault` and create files from inline content or an input file, but it does not include any warning or caution about filesystem modification. Under the markdown-specific warning rule, descriptions of behaviors that affect user data or system state should disclose that effect.

Static analysis

No suspicious patterns detected.