Back to skill

Security audit

Bazi Analysis

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local Bazi chart generator with no evidence of hidden access, persistence, exfiltration, or destructive behavior.

Install only if you are comfortable running a local Python charting script on birth date, birth time, and gender inputs. Treat the output as traditional/metaphysical interpretation, not professional advice, and avoid exposing the script directly to untrusted users unless --years and supported year ranges are bounded.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bazi_chart.py:112
Finding
Unbounded Year-Range Input Can Cause Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bazi_chart.py`, lines 112–116; related argument definition at line 224 **Vulnerability Type**: Uncontrolled resource consumption **Risk Level**: Medium ### Vulnerable Code ```python start_year = from_year or datetime.now().year liunian = [] for yy in range(start_year, start_year + max(1, years)): # Calculate the annual stem and branch using the midpoint of the Gregorian year ysolar = Solar.fromYmdHms(yy, 6, 30, 12, 0, 0) yl = ysolar.getLunar().getEightChar().getYear() liunian.append({'year': yy, 'ganzhi': yl}) ``` The corresponding command-line argument has no upper bound: ```python ap.add_argument('--years', type=int, default=10, help='Generate annual results; default: 10 years') ``` ### Technical Analysis The user-controlled `--years` argument determines the number of iterations performed by the annual-results loop. Although `max(1, years)` prevents zero or negative iteration counts, the code does not impose an upper limit. Every iteration performs calendar conversion through `lunar_python` and appends a new dictionary to the in-memory `liunian` list. A sufficiently large value can therefore cause excessive CPU use and continuous memory growth. Processing ends only when the loop completes, the dependency rejects an unsupported year, or the process exhausts available resources. The issue is reachable through the documented command-line interface. Its practical severity depends on how the script is deployed: impact is limited for trusted local use but becomes more significant if an Agent, API, queue worker, or other automated service passes untrusted values to the script. ### Attack Path 1. An attacker or untrusted caller gains control over the `--years` argument. 2. The caller invokes the script with an excessive value, for example: ```bash python scripts/bazi_chart.py \ --date 1989-10-17 \ --time 12:00 \ --gender male \ --years 1000000000 ``` ...[truncated 1283 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce a strict application-level limit in `build()` so callers cannot bypass command-line validation: ```python MAX_YEARS = 100 if not 1 <= years <= MAX_YEARS: raise ValueError(f"years must be between 1 and {MAX_YEARS}") ``` 2. Add an `argparse` validator to reject invalid values before processing: ```python def bounded_year_count(value: str) -> int: count = int(value) if not 1 <= count <= 100: raise argparse.ArgumentTypeError("years must be between 1 and 100") return count ap.add_argument( '--years', type=bounded_year_count, default=10, help='Number of annual results to generate, from 1 to 100' ) ``` 3. Validate `--from-year` and the computed ending year against the calendar range supported by `lunar_python`. 4. If invoked by a network-facing or multi-user service, also enforce request timeouts, per-user rate limits, memory limits, and CPU quotas. These controls should supplement rather than replace input validation. 5. Add regression tests covering zero, negative, maximum permitted, above-maximum, and unsupported year-range inputs. ]]>
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (14)

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The manifest promises advanced Bazi features such as polished consultation-style interpretation, ten gods, hidden stems, luck cycles, and yearly outlook, but the documentation says the implementation only uses EightChar plus simplified five-element counting without hidden stem weighting or advanced calculations. In a decision-support context, this is dangerous because users may receive authoritative-seeming output that omits core mechanics, leading to materially misleading results and overreliance.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
The notes explicitly state the skill is only a basic charting and rough five-element analysis version, while the manifest markets it as a professional consultation tool with advanced outputs. This discrepancy can mislead users and downstream agents into over-trusting the completeness and rigor of the analysis, creating integrity and safety risks when users rely on outputs for consequential decisions.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This code file contains user-facing natural-language content such as the module description and generated output labels in Chinese, but it does not indicate that the language is optional or region-specific. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
def pillar_bundle(ec, key: str) -> Dict[str, Any]:
    title = key.capitalize()
    gz = getattr(ec, f"get{title}")()
    gan = getattr(ec, f"get{title}Gan")()
    zhi = getattr(ec, f"get{title}Zhi")()
    return {
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_bundle(ec, key: str) -> Dict[str, Any]:
    title = key.capitalize()
    gz = getattr(ec, f"get{title}")()
    gan = getattr(ec, f"get{title}Gan")()
    zhi = getattr(ec, f"get{title}Zhi")()
    return {
        "ganzhi": gz,
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
title = key.capitalize()
    gz = getattr(ec, f"get{title}")()
    gan = getattr(ec, f"get{title}Gan")()
    zhi = getattr(ec, f"get{title}Zhi")()
    return {
        "ganzhi": gz,
        "gan": gan,
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
"ganzhi": gz,
        "gan": gan,
        "zhi": zhi,
        "wuxing": getattr(ec, f"get{title}WuXing")(),
        "nayin": getattr(ec, f"get{title}NaYin")(),
        "xun": getattr(ec, f"get{title}Xun")(),
        "xunkong": getattr(ec, f"get{title}XunKong")(),
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": gan,
        "zhi": zhi,
        "wuxing": getattr(ec, f"get{title}WuXing")(),
        "nayin": getattr(ec, f"get{title}NaYin")(),
        "xun": getattr(ec, f"get{title}Xun")(),
        "xunkong": getattr(ec, f"get{title}XunKong")(),
        "dishi": getattr(ec, f"get{title}DiShi")(),
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
"zhi": zhi,
        "wuxing": getattr(ec, f"get{title}WuXing")(),
        "nayin": getattr(ec, f"get{title}NaYin")(),
        "xun": getattr(ec, f"get{title}Xun")(),
        "xunkong": getattr(ec, f"get{title}XunKong")(),
        "dishi": getattr(ec, f"get{title}DiShi")(),
        "shishen_gan": getattr(ec, f"get{title}ShiShenGan")(),
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
"wuxing": getattr(ec, f"get{title}WuXing")(),
        "nayin": getattr(ec, f"get{title}NaYin")(),
        "xun": getattr(ec, f"get{title}Xun")(),
        "xunkong": getattr(ec, f"get{title}XunKong")(),
        "dishi": getattr(ec, f"get{title}DiShi")(),
        "shishen_gan": getattr(ec, f"get{title}ShiShenGan")(),
        "shishen_zhi": getattr(ec, f"get{title}ShiShenZhi")(),
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
"nayin": getattr(ec, f"get{title}NaYin")(),
        "xun": getattr(ec, f"get{title}Xun")(),
        "xunkong": getattr(ec, f"get{title}XunKong")(),
        "dishi": getattr(ec, f"get{title}DiShi")(),
        "shishen_gan": getattr(ec, f"get{title}ShiShenGan")(),
        "shishen_zhi": getattr(ec, f"get{title}ShiShenZhi")(),
        "hide_gan": getattr(ec, f"get{title}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
"xun": getattr(ec, f"get{title}Xun")(),
        "xunkong": getattr(ec, f"get{title}XunKong")(),
        "dishi": getattr(ec, f"get{title}DiShi")(),
        "shishen_gan": getattr(ec, f"get{title}ShiShenGan")(),
        "shishen_zhi": getattr(ec, f"get{title}ShiShenZhi")(),
        "hide_gan": getattr(ec, f"get{title}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
"xunkong": getattr(ec, f"get{title}XunKong")(),
        "dishi": getattr(ec, f"get{title}DiShi")(),
        "shishen_gan": getattr(ec, f"get{title}ShiShenGan")(),
        "shishen_zhi": getattr(ec, f"get{title}ShiShenZhi")(),
        "hide_gan": getattr(ec, f"get{title}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
"dishi": getattr(ec, f"get{title}DiShi")(),
        "shishen_gan": getattr(ec, f"get{title}ShiShenGan")(),
        "shishen_zhi": getattr(ec, f"get{title}ShiShenZhi")(),
        "hide_gan": getattr(ec, f"get{title}HideGan")(),
    }
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.