Back to skill

Security audit

BaZi Partner 八字搭档匹配

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for BaZi partner matching, but it can persistently change the user's OpenClaw persona file with caller-supplied prompt text, so it belongs in Review.

Install only if you intentionally want this MCP server to be able to alter OpenClaw's persistent persona file. Do not allow automatic use of bazi_apply_prompt unless the exact prompt and destination are shown first, and be prepared to remove the marked bazi-partner section from SOUL.md if you no longer want the behavior. Prefer a pinned package or reviewed release over the mutable git install command.

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
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T01 · Skill Instruction Hijacking

Error
Location
src/mcp_bazi_partner/server.py:180
Finding
Persistent Modification of Agent Personality and Instructions<![CDATA[ ## Vulnerability Details **File Location**: `src/mcp_bazi_partner/server.py:180-229` **Vulnerability Type**: Persistent agent instruction modification **Risk Level**: High ### Vulnerable Code ```python @mcp.tool() def bazi_apply_prompt(system_prompt: str, partner_type: str = "") -> str: """Append the matched partner's system prompt into the user's SOUL.md file. This makes the OpenClaw agent adopt the BaZi partner personality. The prompt is APPENDED (not overwritten) to ~/.openclaw/SOUL.md using markers to safely replace any previous bazi-partner section. """ home = Path.home() candidates = [ home / ".openclaw" / "SOUL.md", home / ".openclaw" / "workspace" / "SOUL.md", ] target = None for path in candidates: if path.exists(): target = path break if target is None: target = candidates[0] target.parent.mkdir(parents=True, exist_ok=True) existing = target.read_text(encoding="utf-8") if target.exists() else "" if _MARKER_START in existing: before = existing[:existing.index(_MARKER_START)] end_idx = existing.index(_MARKER_END) + len(_MARKER_END) after = existing[end_idx:] existing = before + after header = f"# BaZi partner personality — {partner_type}\n\n" if partner_type else "" new_section = _MARKER_START + header + system_prompt + _MARKER_END target.write_text(existing + new_section, encoding="utf-8") ``` ### Technical Analysis The tool intentionally writes behavioral instructions into OpenClaw's persistent `SOUL.md` state. The prompts bundled in `src/mcp_bazi_partner/data/partner_prompts.json` direct the agent to change its reasoning style, response style, initiative, and decision-making behavior. Unlike a temporary response preference, modifying `SOUL.md` affects later conversations after the BaZi operation has completed. The implementation therefore crosses the boundary between gene ...[truncated 1499 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove direct modification of `SOUL.md` from the skill. 2. Return the matched personality as display-only content or as a temporary session preference. 3. If persistence is essential, store the selection in a dedicated, non-authoritative skill configuration file rather than an agent instruction file. 4. Make persistent activation explicitly revocable and provide a corresponding removal tool. 5. Display the exact content, destination file, and persistence implications before requesting consent. 6. Keep skill-generated personality preferences subordinate to platform safety rules and user instructions. 7. Add integration tests confirming that ordinary analysis and matching operations never modify persistent agent state. ]]>

T02 · Agent Memory Poisoning

Error
Location
src/mcp_bazi_partner/server.py:180
Finding
Caller-Controlled Prompt Can Be Written Directly to Persistent Agent State<![CDATA[ ## Vulnerability Details **File Location**: `src/mcp_bazi_partner/server.py:180-229` **Vulnerability Type**: Unvalidated persistent prompt injection **Risk Level**: Critical ### Vulnerable Code ```python @mcp.tool() def bazi_apply_prompt(system_prompt: str, partner_type: str = "") -> str: home = Path.home() candidates = [ home / ".openclaw" / "SOUL.md", home / ".openclaw" / "workspace" / "SOUL.md", ] target = None for path in candidates: if path.exists(): target = path break if target is None: target = candidates[0] target.parent.mkdir(parents=True, exist_ok=True) existing = target.read_text(encoding="utf-8") if target.exists() else "" if _MARKER_START in existing: before = existing[:existing.index(_MARKER_START)] end_idx = existing.index(_MARKER_END) + len(_MARKER_END) after = existing[end_idx:] existing = before + after header = f"# BaZi partner personality — {partner_type}\n\n" if partner_type else "" new_section = _MARKER_START + header + system_prompt + _MARKER_END target.write_text(existing + new_section, encoding="utf-8") ``` ### Technical Analysis The MCP tool accepts `system_prompt` directly from its caller and writes that value into persistent agent state without verifying its origin or content. The implementation does not require the prompt to match one of the trusted entries bundled in `partner_prompts.json`. The requirement to obtain user confirmation exists only in the tool documentation and workflow instructions. It is not enforced by code. An MCP client, another agent, or a compromised orchestration layer can invoke `bazi_apply_prompt` directly without first calling `bazi_partner` or obtaining confirmation. There is also no consent token, prompt identifier, cryptographic binding, allowlist comparison, length restriction, or rejection of nested marker content. Consequently, this function ...[truncated 1608 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate the free-form `system_prompt` argument. 2. Accept only a strict partner prompt identifier and resolve the associated content from the packaged, reviewed dataset. 3. Validate the identifier against an explicit allowlist. 4. Require a short-lived consent token generated only after displaying the exact prompt to the user. 5. Bind the consent token to the prompt identifier, destination, user, and expiration time. 6. Reject direct tool invocation when no valid consent token is supplied. 7. Reject marker strings and other control syntax in all user-influenced metadata. 8. Apply conservative maximum lengths to `partner_type` and any remaining text fields. 9. Store preferences in a dedicated structured configuration file rather than `SOUL.md`. 10. Record an audit event containing the selected identifier and consent state, while avoiding storage of unnecessary personal birth information. 11. Add tests proving that arbitrary prompt text and direct calls without verified consent are rejected. ]]>

T08 · Insecure Dependencies

Note
Location
pyproject.toml:1
Finding
Dependency and Installation Sources Are Not Pinned to Immutable Artifacts<![CDATA[ ## Vulnerability Details **File Location**: `pyproject.toml:1-3,24-27` **Vulnerability Type**: Unpinned third-party dependency resolution **Risk Level**: Low ### Vulnerable Code ```toml [build-system] requires = ["hatchling"] build-backend = "hatchling.build" dependencies = [ "mcp[cli]>=1.0.0", "lunar-python>=1.3.5", ] ``` Related installation guidance in `README.md:11-15`: ```bash pip install git+https://github.com/ZoezoeCookie/mcp-bazi-partner.git ``` ### Technical Analysis The build and runtime dependencies use either no version constraint or lower-bound-only constraints. This permits future installations to resolve versions that were not part of the reviewed project state. The documented Git installation command also tracks the repository's default branch rather than an immutable commit or signed release tag. Code obtained through that command can therefore change after review. No evidence was found that the currently named dependencies are malicious, typosquatted, or performing unauthorized actions. This finding concerns supply-chain reproducibility and exposure to future upstream compromise rather than a confirmed malicious dependency. ### Attack Path 1. A developer follows the documented installation command or installs the package in a fresh environment. 2. The package manager resolves the latest dependency versions permitted by the lower-bound constraints, or Git retrieves the current default branch. 3. An upstream package or repository is compromised, maliciously updated, or introduces an incompatible security regression. 4. The changed code is installed despite not being part of the audited artifact. 5. Build hooks or runtime imports execute the changed upstream code with the installing user's privileges. ### Impact Assessment Potential impact is limited by the privileges of the installation or runtime process, but dependency code can generally execute arbitrary Python code in that context. A compromised dependency cou ...[truncated 278 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin reviewed dependency versions in a lockfile used for deployment. 2. Use hash-verified installation for released artifacts. 3. Pin the build backend to a reviewed compatible version. 4. Replace default-branch Git installation instructions with a versioned package release or immutable commit hash. 5. Prefer signed release tags and publish integrity metadata where supported. 6. Automate dependency vulnerability scanning and controlled update review. 7. Generate reproducible builds and retain software bill-of-materials data for published releases. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (24)

YARA rule 'agent_skill_remote_bootstrap_execution': Remote script or code download followed by execution/bootstrap installation [agent_skills]

High
Category
YARA Match
Content
# mcp-bazi-partner

MCP server for Chinese BaZi (Four Pillars of Destiny) analysis and AI partner matching.

## Install

```bash
pip install mcp-bazi-partner
```

Or install from source:

```bash
pip install git+https://github.com/ZoezoeCookie/mcp-bazi-partner.git
```

## Usage

### As MCP server (for OpenClaw / Claude Code)

```json
{
  "mcpServers": {
    "bazi-partner": {
      "command": "mcp-bazi-partner"
    }
  }
}
```

### Tools

- **bazi_analyze** — Input birth date, get four pillars + pattern determination
- **bazi_partner** — Input pattern result, get matched AI partner type + system prompt

## Method

Based on Shen Xiaozhan's "Ziping Zhenquan" (子平真诠) pattern method.
Covers 38 pattern sub-types × 3 status types = 114 combinations.
Confidence
85% confidence
Finding
The README instructs users to install directly from a GitHub repository via `pip install git+https://...`, which causes code from a mutable remote source to be fetched and executed during installation. This creates a significant supply-chain risk because a compromised repository, force-pushed branch, or malicious dependency change could deliver arbitrary code to anyone following the instructions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared purpose suggests horoscope-style analysis and partner matching, but the skill also persists a system prompt/persona into SOUL.md, altering future assistant behavior. This mismatch is dangerous because users may consent to a harmless-seeming metaphysical analysis without realizing it changes the assistant's persistent instruction layer.

YARA rule 'agent_skill_remote_bootstrap_execution': Remote script or code download followed by execution/bootstrap installation [agent_skills]

High
Category
YARA Match
Content
ir AI companion type based on birth date.
metadata:
  openclaw:
    emoji: 🎴
    homepage: https://github.com/ZoezoeCookie/mcp-bazi-partner
    requires:
      bins:
        - python3
---

# BaZi Partner — 八字命理搭档匹配

根据用户的出生年月日时,使用中国传统八字命理(子平格局法)进行排盘分析,并匹配专属 AI 搭档类型。

## 安装

```bash
pip install git+https://github.com/ZoezoeCookie/mcp-bazi-partner.git
```

## 三步自动流程(重要!)

当用户提供生日信息时,**必须自动串联执行以下三步**,不要等用户逐步指示:

### Step 1: 排盘分析
调用 `bazi_analyze`,传入年月日时。

**关键:必须询问用户出生的具体小时(0-23点)。**
时辰决定时柱,直接影响格局判定。如果用户不知道具体时间,告知结果可能不准确。

### Step 2: 判定子格局 + 搭档匹配

**关键!** `bazi_analyze` 返回的 `pattern.final_pattern` 是大格局(L
Confidence
93% confidence
Finding
The skill instructs users to install directly from a remote Git repository using pip, which bootstraps unpinned code from a mutable source. If the repository or dependency chain is compromised, this can lead to arbitrary code execution during installation in the user's environment.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill can read/write local files, including persisting changes to SOUL.md, but it declares no explicit tool scope or permissions boundary. That creates an authorization gap where users and hosts are not clearly informed that the skill has filesystem-modifying capability, increasing the chance of silent or over-broad file access.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
Writing to SOUL.md changes the assistant's persistent persona/system prompt, which can influence future conversations beyond the current task. Without a clear warning about persistence and behavioral impact, users may unknowingly authorize durable prompt injection into their assistant environment.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
This JSON file contains all user-facing intro text exclusively in Chinese, with no indication elsewhere in the file that language selection is optional or configurable. Under the policy, forcing a specific language without user opt-in is a natural-language locale violation.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
This JSON file uses Chinese-only keys and descriptive text throughout, with no indication that the skill offers users a language choice or that the locale restriction is explicitly documented as optional. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
This JSON file contains all prompt instructions exclusively in Chinese and provides no natural-language indication that language choice is optional or tied to a clearly documented region-specific use case. Under the policy, forcing a specific language or locale without user opt-in is a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This code hard-codes user-facing labels and outputs in Chinese across pattern names, shichen names, Ten Gods results, and status display text. Because the file is code, SQP-3 applies to natural-language strings here, and there is no indication that users can opt into this locale or that the skill is explicitly limited to a Chinese-language/regional context.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This code returns a user-facing string literal in Chinese ("全球仅 ... 的人同款") and also constructs Chinese-labeled partner types, which imposes a specific language/locale in output. The file does not indicate that the skill is region-specific or that users can opt into this locale, so it matches the language policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The module description, operational guidance, and later tool instructions/messages are written primarily in Chinese, and the skill does not indicate that users may choose another language. Under the policy, forcing a specific language without opt-in is a natural-language policy violation unless clearly documented as region-specific or optional.

Session Persistence

Medium
Category
Rogue Agent
Content
intro text, and system prompt to inject into a custom AI assistant.

    After getting the result, you MUST ask the user for confirmation before
    calling bazi_apply_prompt to write the partner personality into SOUL.md.

    Args:
        sub_type: Pattern sub-type name (e.g. "煞印相生", "正官格", "食神生财")
Confidence
89% confidence
Finding
The skill persists a generated `system_prompt` into `~/.openclaw/SOUL.md`, which can alter future agent behavior across sessions. Because the persisted content is dynamic and comes from tool output, this creates a prompt-persistence mechanism that can become a durable prompt-injection or unsafe-behavior vector if upstream content is incorrect, manipulated, or overly permissive.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The natural-language instructions, headings, and examples are all in Chinese, which can effectively force a specific language/locale experience without user opt-in. The policy allows language constraints when the user is given a choice or the locale limitation is clearly documented and justified, which is not stated here.

Unverifiable Dependency: mcp has 12 known advisory(ies) (CVE-2025-53366 (MCP Python SDK vulnerability in the FastMCP Server causes validation error, lead); CVE-2025-66416 (Model Context Protocol (MCP) Python SDK does not enable DNS rebinding protection); CVE-2026-52870 (MCP Python SDK: Experimental task handlers allow any client to access and cancel) +9 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
92% confidence
Finding
The dependency specification `mcp[cli]>=1.0.0` allows installation of any newer release without an upper bound or exact pin, so consumers may resolve to vulnerable versions of the MCP SDK and its CLI extras. Because the static finding cites multiple known advisories affecting `mcp`, and the manifest does not constrain to a verified fixed version, the package supply chain risk is real even though the file itself does not prove exploitation.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The module docstring describes the skill specifically as a 'Chinese BaZi partner matching server,' which indicates a locale/cultural constraint in natural language without any visible user choice or opt-in. Under the stated policy, locale-specific behavior should either offer user choice or clearly justify the constraint.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
This JSON file consists entirely of Chinese natural-language category labels and values, with no indication that the skill offers a language choice or that the locale restriction is intentionally region-specific. Under the policy, hard-coding a single language without opt-in can be a natural-language locale violation.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
bazi.setSect(1)

    def _pillar(pos: str) -> Pillar:
        gz = getattr(bazi, f"get{pos}")()
        gan = getattr(bazi, f"get{pos}Gan")()
        zhi = getattr(bazi, f"get{pos}Zhi")()
        hide_gan = list(getattr(bazi, f"get{pos}HideGan")())
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
def _pillar(pos: str) -> Pillar:
        gz = getattr(bazi, f"get{pos}")()
        gan = getattr(bazi, f"get{pos}Gan")()
        zhi = getattr(bazi, f"get{pos}Zhi")()
        hide_gan = list(getattr(bazi, f"get{pos}HideGan")())
        nayin = getattr(bazi, f"get{pos}NaYin")()
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
def _pillar(pos: str) -> Pillar:
        gz = getattr(bazi, f"get{pos}")()
        gan = getattr(bazi, f"get{pos}Gan")()
        zhi = getattr(bazi, f"get{pos}Zhi")()
        hide_gan = list(getattr(bazi, f"get{pos}HideGan")())
        nayin = getattr(bazi, f"get{pos}NaYin")()
        return _make_pillar(gz, gan, zhi, hide_gan, nayin)
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
gz = getattr(bazi, f"get{pos}")()
        gan = getattr(bazi, f"get{pos}Gan")()
        zhi = getattr(bazi, f"get{pos}Zhi")()
        hide_gan = list(getattr(bazi, f"get{pos}HideGan")())
        nayin = getattr(bazi, f"get{pos}NaYin")()
        return _make_pillar(gz, gan, zhi, hide_gan, nayin)
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
gan = getattr(bazi, f"get{pos}Gan")()
        zhi = getattr(bazi, f"get{pos}Zhi")()
        hide_gan = list(getattr(bazi, f"get{pos}HideGan")())
        nayin = getattr(bazi, f"get{pos}NaYin")()
        return _make_pillar(gz, gan, zhi, hide_gan, nayin)

    year_p = _pillar("Year")
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Rp1

Low
Category
MCP Rug Pull
Confidence
60% confidence
Finding
pip install without ==version installs the latest release, which could include malicious changes.

Rp1

Low
Category
MCP Rug Pull
Confidence
60% confidence
Finding
pip install without ==version installs the latest release, which could include malicious changes.

Rp1

Low
Category
MCP Rug Pull
Confidence
60% confidence
Finding
pip install without ==version installs the latest release, which could include malicious changes.

Static analysis

No suspicious patterns detected.