Back to skill

Security audit

llm-wiki SKILL inspired by Karpathy

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly fits its wiki/Zotero purpose, but it needs review because some install and Zotero/MCP paths can expose credentials or write through a different backend than the user selected.

Install only from a pinned release or reviewed commit, avoid copy-pasting remote installer pipes, and treat Zotero/MCP configuration as security-sensitive. Before enabling Zotero writes, prefer memory-only authorization, verify the target library and backend, and be aware that the current `zotero-refresh --write-backend local` option appears not to be honored by the inspected CLI path.

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

T03 · Remote Payload Retrieval and Execution

Error
Location
README.md:27
Finding
Remote Installer Content Is Executed Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `README.md:27-30` **Vulnerability Type**: Remote payload retrieval and immediate execution **Risk Level**: Critical ### Vulnerable Code ```bash # 1. Install uv once (https://docs.astral.sh/uv/) # Windows: powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # macOS/Linux: curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### Technical Analysis The documented installation commands retrieve mutable content from an external server and immediately pass it to a PowerShell or POSIX shell interpreter. The downloaded installer is not saved for inspection, pinned to a version, or verified using a cryptographic checksum or signature. The referenced domain is consistent with the official Astral `uv` installer, and the audit found no evidence that the current project intentionally controls or replaces that payload. Nevertheless, the effective code executed by the user can change after this Skill has been reviewed. Trust in the repository alone is therefore insufficient to establish the safety of the executed installer. This behavior exceeds the minimum privilege required to explain how to install `uv`: installation can instead use a trusted package manager or a separately downloaded and verified artifact. ### Attack Path 1. A user follows the Quick Start instructions. 2. An attacker compromises the installer publication process, remote hosting account, DNS/TLS trust path, or another relevant distribution component. 3. The remote endpoint returns attacker-controlled shell or PowerShell content. 4. `curl` or `irm` retrieves the modified content. 5. The pipeline passes it directly to `sh` or `iex`. 6. The payload executes with all privileges available to the invoking user. ### Impact Assessment A successful attack provides arbitrary code execution under the user account running the installation command. The payload could read or alter files accessible to that account, collect environment credential ...[truncated 200 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all `curl | sh` and `irm | iex` installation examples. 2. Prefer installation through a trusted, platform-specific package manager with versioned packages. 3. If a standalone installer is necessary: - Download it to a local file without executing it. - Pin the installer to a specific release. - Obtain the expected checksum or signature through an independently authenticated release channel. - Verify the artifact before execution. - Run the verified local file as a separate command. 4. Document the expected publisher, release version, checksum algorithm, and verification command. 5. Advise users not to run installation commands from an elevated shell unless elevation is explicitly necessary. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:32
Finding
Installation Uses Mutable Git State and Unbounded Future Dependency Versions<![CDATA[ ## Vulnerability Details **File Locations**: - `README.md:32-46` - `src/requirements.txt:1-7` **Vulnerability Type**: Unpinned software supply chain **Risk Level**: Medium ### Vulnerable Code From `README.md`: ```bash # 2. Install llm-wiki straight from GitHub (uv fetches it for you) uv tool install git+https://github.com/Nemo4110/llm-wiki.git # or run without installing: uvx --from git+https://github.com/Nemo4110/llm-wiki.git llm-wiki --help ``` The documented upgrade process also retrieves the latest default-branch state: ```text Upgrading later is one command: `uv tool upgrade llm-wiki` (re-fetches the latest commit from the default branch). ``` From `src/requirements.txt`: ```text pyyaml>=6.0 pymupdf>=1.25.0 numpy>=1.24.0 httpx>=0.27.0 mcp>=1.0.0 openai>=1.0.0 pytest>=7.0.0 ``` ### Technical Analysis The Git installation commands do not specify a release tag or immutable commit hash. Consequently, the same command can install different source code depending on the current state of the repository's default branch. The dependency specifications use lower bounds without upper bounds, exact versions, or hashes. A future release of any dependency may therefore be selected automatically. This prevents reproducible installation and expands trust from the audited source tree to all future compatible releases of each package. No typosquatted or demonstrably malicious package was identified during the audit. The vulnerability is the absence of immutable resolution and integrity controls, which creates an exploitable supply-chain path if an upstream repository or package-publishing account is compromised. ### Attack Path 1. An attacker compromises the project repository, an authorized maintainer account, a dependency publisher, or a package distribution account. 2. The attacker publishes a malicious default-branch commit or a higher dependency version that satisfies the broad version constraint. 3. A user runs the documented installation or upg ...[truncated 654 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Publish versioned releases and direct normal users to install a specific release rather than the mutable default branch. 2. Pin Git installations to both a reviewed release tag and its immutable commit hash. 3. Generate and maintain a lockfile containing exact transitive dependency versions. 4. For supported installers, require package hashes so downloaded artifacts are cryptographically verified. 5. Separate development dependencies such as `pytest` from runtime dependencies. 6. Use an automated dependency-update process that creates reviewable changes, runs tests and security scanning, and updates the lockfile explicitly. 7. Publish signed release tags or attestations and document how users can verify them. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/llm_wiki/zotero/mcp_client.py:47
Finding
Configuration-Selected MCP Process Inherits the Entire Parent Environment<![CDATA[ ## Vulnerability Details **File Location**: `src/llm_wiki/zotero/mcp_client.py:47-67` **Vulnerability Type**: Excessive credential exposure to a child process **Risk Level**: Medium ### Vulnerable Code ```python servers = raw.get("mcpServers", raw) server = servers.get(self.server_name) if not isinstance(server, Mapping): raise ZoteroMCPError( f"MCP server {self.server_name!r} is not configured in {self.config_path}" ) command = str(server.get("command") or "").strip() if not command: raise ZoteroMCPError(f"MCP server {self.server_name!r} has no command") env = {**os.environ} env.update( {str(key): str(value) for key, value in (server.get("env") or {}).items()} ) params = StdioServerParameters( command=command, args=[str(value) for value in (server.get("args") or [])], env=env, ) ``` ### Technical Analysis The MCP executable and its arguments are selected from an external configuration file. Before launching that executable, the implementation copies every variable from `os.environ` into the child environment and then adds the server-specific variables. An MCP server may legitimately require selected Zotero configuration values. It does not require unrelated credentials belonging to cloud providers, source-control systems, CI services, or other tools in the Agent host. Passing the complete environment therefore violates least-privilege principles and unnecessarily enlarges the trust boundary around the configured executable. The audit did not find code in this repository that transmits inherited credentials. Exploitation requires a malicious, compromised, or incorrectly replaced MCP command. However, once such a command is selected, the environment inheritance gives it direct access to all parent-process environment secrets. ### Attack Path 1. An attacker modifies the MCP configuration file, replaces the configured executable, compromises its package, or causes command resolution to select an attacker-contr ...[truncated 870 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace full environment inheritance with an explicit allowlist. 2. Include only essential process variables, such as a trusted `PATH`, required locale variables, and specifically approved Zotero/MCP variables. 3. Do not automatically forward unrelated `*_TOKEN`, `*_SECRET`, `*_PASSWORD`, or `*_API_KEY` values. 4. Resolve the configured executable to an absolute path and validate it against an approved executable or installation location. 5. Pin and verify the supported MCP server version. 6. Reject malformed `env` configuration values and consider maintaining a fixed allowlist of permitted server-specific variable names. 7. Document that MCP configuration is security-sensitive and must not be writable by untrusted users. 8. Where practical, run the MCP process with additional operating-system sandboxing and restricted filesystem/network permissions. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (95)

Chaining Abuse

High
Category
Tool Misuse
Content
```bash
# 1. Install uv once (https://docs.astral.sh/uv/)
#    Windows:  powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
#    macOS/Linux: curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Install llm-wiki straight from GitHub (uv fetches it for you)
uv tool install git+https://github.com/Nemo4110/llm-wiki.git
Confidence
94% confidence
Finding
The explicit `| sh` pattern is a direct remote-code-execution anti-pattern because it removes the user's chance to inspect the downloaded script before execution. In this skill's context, the danger is amplified: the project is designed for AI-agent-assisted command execution, so unsafe installation guidance can be copied or enacted by automation with less scrutiny than a human would apply.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description centers on runtime llm-wiki knowledge-base operations: ingesting source files, answering questions from wiki pages, running agent-bridge commands, preserving provenance metadata, and interacting with Zotero. The code shown does none of those things. Instead, it performs software packaging and release assembly. While it writes a starter wiki/index.md and includes project files, that is incidental to building a distributable release bundle, not to operating the wiki itself. This is a materially different primary purpose, so the description does not accurately represent the code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description covers broad llm-wiki knowledge-base operations such as wiki ingestion, wiki question answering, agent-bridge status/lint/link/relink/merge/query/index tasks, provenance handling, and Zotero-based literature discovery. The actual code shown does not implement those functions. Instead, it provides a command-line helper for reading PDF text and extracting images/figures from PDFs. While PDF processing could be a supporting component of a larger ingestion pipeline, this chunk’s primary behavior is specifically PDF content/image extraction, which is not explicitly represented in the declared purpose and is materially narrower/different from the stated skill behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description focuses on operating an llm-wiki knowledge base and related agent-bridge/Zotero workflows. The supplied code does not implement wiki ingestion, wiki question answering, provenance management, indexing, linking, querying, or Zotero relocation/discovery. Instead, it performs a distinct document-processing task: extracting images and rendered vector figures from PDF files to disk. This is a materially different primary purpose and an undeclared capability, so the description does not accurately represent the code chunk.

Hidden Instructions

High
Category
Prompt Injection
Content
## 知识主体

<!--
根据来源内容地图选择并命名章节,不要机械保留下列所有示例:
- 根本问题与关键矛盾
- 机制、推导或系统数据流
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
## 知识主体

<!--
根据来源内容地图选择并命名章节,不要机械保留下列所有示例:
- 根本问题与关键矛盾
- 机制、推导或系统数据流
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
## 知识主体

<!--
根据来源内容地图选择并命名章节,不要机械保留下列所有示例:
- 根本问题与关键矛盾
- 机制、推导或系统数据流
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
## 知识主体

<!--
根据来源内容地图选择并命名章节,不要机械保留下列所有示例:
- 根本问题与关键矛盾
- 机制、推导或系统数据流
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Agent Config Directory Access

High
Category
Agent Snooping
Content
Do not assume that `~/.config/zotero-mcp/config.json` controls the active MCP access mode. That file primarily stores semantic-search, database-path, extraction, and index-update settings. Local/Web mode and Web credentials are normally supplied by the MCP client's process environment. However, zotero-mcp v0.11.0 was observed to **also load Web credentials from that file's `client_env` block** when the process environment does not provide them — so treat the file as a potential credential source, and do not assume that "no `ZOTERO_API_KEY` in the client process env" means a write will stay local or fail.

For Codex, inspect the configured server entry in `~/.codex/config.toml` (or the equivalent host-managed MCP configuration). A hybrid configuration keeps local access enabled while retaining Web credentials for write-capable operations:

```toml
[mcp_servers.zotero-mcp]
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The CLI advertises `--write-backend web|local`, implying operators can keep writes on the MCP/web path, but the implementation for `zotero-refresh --apply-safe` ignores that control and always follows the local writer flow. In a security-sensitive agent skill, misleading write-path controls can cause unexpected direct local API mutations, bypassing intended operational boundaries, audit assumptions, or approval expectations.

Self-Modification

High
Category
Rogue Agent
Content
"target_dir", nargs="?", default=".", help="Directory to initialize"
    )
    init_parser.add_argument(
        "--force", action="store_true", help="Overwrite existing files"
    )

    # ingest
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.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
if not command:
            raise ZoteroMCPError(f"MCP server {self.server_name!r} has no command")

        env = {**os.environ}
        env.update(
            {str(key): str(value) for key, value in (server.get("env") or {}).items()}
        )
Confidence
98% confidence
Finding
Using the entire process environment as the basis for a child-process environment is a classic secret-exposure pattern because it forwards potentially sensitive variables to code outside the current trust boundary. Here that boundary is especially important because the MCP server executable is selected from configuration and then executed, so any malicious or swapped server binary gains access to inherited credentials immediately at startup.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
The document instructs agents to start a section with `### 时间定位` / `### Temporal Position`, but the later detailed formatting guidance repeatedly mandates Chinese-first heading conventions such as `### 时间定位` and `### 来源摘要`. This creates a language/locale constraint in user-facing wiki content without explicit user opt-in or a documented region-specific justification.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
These lines require agents to use Chinese labels like `时间范围`, `阶段判断`, and `时间信息` in generated markdown structure. That is a natural-language locale policy constraint and may force output into a specific language even when the user has not requested it.

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding
The README recommends installing/running code directly from a Git repository default branch via `uvx --from git+https://github.com/Nemo4110/llm-wiki.git`, without pinning a tag or commit. That creates a supply-chain risk: future upstream compromise, force-pushes, or malicious commits could change what users execute, and the risk is higher here because this skill is explicitly meant for agent/tool execution.

Intent-Code Divergence

Medium
Confidence
86% confidence
Finding
The decision record at L202-L215 sets an explicit boundary: llm-wiki should keep Zotero connectivity in an external Agent skill workflow and not claim unsupported native integration. However, L221-L229 later expands scope to a worker that launches Zotero MCP and performs external DOI/citation/metrics refreshes using Crossref and OpenAlex, which materially broadens the integration model beyond the earlier stated boundary and creates a documentation-level contradiction about what is and is not part of llm-wiki.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill requests or implies broad capabilities including shell, network, file read/write, environment access, and MCP operations, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates an overbroad trust boundary: an agent may invoke powerful tools during normal skill execution without a machine-readable restriction layer, increasing the chance of unintended filesystem modification, network access, or external-system writes.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The file title and all operative guidance are written as mandatory Chinese-language instructions, with no indication that the user can choose another language or locale. Under the policy scope, forcing a specific language without opt-in is a natural-language policy violation unless the locale restriction is explicitly justified.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The template's instructional and content text is written in Chinese and directs authors to produce page content in Chinese, such as the summary and section guidance. This constitutes a language/locale policy concern because it imposes a specific language without offering user choice or documenting a justified region-specific requirement.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The file’s instructional content is written as prescriptive guidance entirely in Chinese, starting with direct requirements about how the summary should be produced. There is no indication that the user may choose another language or that the Chinese-only constraint is required for a region-specific or compliance-related purpose, which can violate language/locale policy expectations.

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
The title and introductory description are written entirely in Chinese, and the rest of the document consistently uses Chinese as the sole language of instruction. Under the policy criteria, forcing a specific language without user opt-in or a documented locale-specific justification can be a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
85% confidence
Finding
The document title and core framing are entirely in Chinese, and it presents the skill architecture evaluation in that language without any indication that language selection is optional. Under the policy rule, forcing a specific language without user opt-in is a natural-language locale violation unless it is clearly documented as region-specific or user-selectable.

Session Persistence

Medium
Category
Rogue Agent
Content
**Local API writes — desktop capability exists; zotero-mcp has not adopted it; llm-wiki adds a temporary direct path (most important):**

- Zotero 10 added **Local API write support** at the desktop level.
- The integrated zotero-mcp (≤ v0.11.0) **still routes its own writes through the Zotero Web API**; local mode remains **read-only for writes** ("fast local reads, web API writes"). This is unchanged.
- Consequence for the MCP path: `metadata_write_backend = local` is **not reachable through zotero-mcp** today; the MCP metadata write gate and the hybrid-mode warning below still stand for anything written via zotero-mcp.
- **Temporary opt-in exception:** llm-wiki now offers a direct local write path that bypasses zotero-mcp only for the exact authorized targets. Collection discovery, snapshots, sources, and bibliographic reads still go through MCP; direct local GETs are permitted only as precondition and post-write verification barriers for those targets. One-time `agent-bridge.py zotero-local-auth` stores a reusable key under gitignored `var/zotero-local.json`; then either `zotero-refresh --apply-safe --write-backend local` or the restricted reviewed `zotero-writeback` workflow writes via the local API. `zotero-writeback --memory-authorize` can instead keep a one-run key only in process memory. See "Temporary Direct Local Writes" below. **Remove this exception once zotero-mcp adopts local writes.**
Confidence
84% confidence
Finding
The document authorizes a reusable local API key to be stored in `var/zotero-local.json` and describes a direct local write path that bypasses the normal MCP boundary. Even though it is gitignored and mode-0600, this creates credential persistence and an alternate write channel; compromise of the workspace or agent runtime could expose a long-lived key with broad write access to editable libraries.

Session Persistence

Medium
Category
Rogue Agent
Content
| Local | Any | Web | `divergent` or `unknown` | Stop writes and reconcile or re-audit first |
| Any | Any | `unavailable` | Read-only work only |

Do not infer backend consistency from configuration values alone. A valid API key, an exposed write tool, or a successful single-item write proves only that one route is writable; it does not prove that local and Web state agree.

> **Note on `metadata_write_backend = local`:** not reachable *through zotero-mcp* — v0.11.0 routes its writes through the Web API even against Zotero 10. The `local` write backend is reachable only via the temporary direct local paths (`zotero-refresh --write-backend local`, reviewed `zotero-writeback`, or reviewed `zotero-relocate`); the gate's Web-backend rows still govern anything written through zotero-mcp. See "Temporary Direct Local Writes".
Confidence
73% confidence
Finding
This section documents and permits temporary direct local write paths outside the normal MCP route (`zotero-refresh --write-backend local`, `zotero-writeback`, `zotero-relocate`). The text includes safety gates, but it still expands the trusted computing boundary and introduces a second mutable backend path, increasing the chance of state divergence, authorization mistakes, or writes occurring through a less-audited channel.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- Never expose Zotero API keys, library credentials, private annotations, or local absolute paths in committed files or logs.
- Do not overwrite item metadata, replace all tags, move collections, attach files, or delete Zotero content without explicit authorization.
- Do not import large item or full-text batches without confirmation.
- Do not treat a search result, abstract, or generated summary as a verified original attachment.
- Do not use untrusted files with unsafe deserialization or execute scripts embedded in source material.
- If item identifiers or titles mismatch, stop ingest and report the expected versus observed metadata.
Confidence
75% 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.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
docs/ZOTERO_MCP_INTEGRATION.md:204