Back to skill

Security audit

Fund Weekly Report

Security checks for vulnerabilities and agentic risk

Overview

The skill is a mostly coherent fund-report generator, but its file access and output behavior exceed the safety limits it claims.

Review before installing. Use only with trusted local inputs, avoid passing arbitrary local paths, and do not run the legacy generator scripts against untrusted requests. The publisher should align SKILL.md with all supported inputs, constrain reads to uploaded files or an approved workspace, prevent report overwrites, and pin dependencies.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/generate_report.py:693
Finding
Arbitrary Local Text File Disclosure Through the Market News Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py:693-704` **Vulnerability Type**: Unrestricted local file read **Risk Level**: Medium ### Vulnerable Code ```python excel_file = sys.argv[1] news_file = sys.argv[2] if len(sys.argv) > 2 else None # Read Excel data excel_data = read_fund_excel(excel_file) # Read market news if provided market_news = [] if news_file: with open(news_file, 'r', encoding='utf-8') as f: market_news = [line.strip() for line in f if line.strip()] # Generate report output_path = generate_weekly_report(excel_data, market_news) print(f"✅ Report generated: {output_path}") ``` The resulting data is incorporated into the document: ```python if market_news: generate_market_news_section(doc, market_news) ``` ### Technical Analysis The optional `news_file` command-line argument is passed directly to Python's `open()` function. The code performs no validation of: - The file extension or actual file type. - Whether the file is inside an authorized workspace. - Whether the path references a sensitive system directory. - Whether the path is a symbolic link to a sensitive file. - The size of the file being read. This behavior conflicts with the security boundary declared in `SKILL.md`, which states that the Skill only reads explicitly provided Excel files, does not read non-Excel files, and does not access sensitive directories. Because every nonempty line is placed into `market_news` and subsequently included in the generated Word report, this is not merely an incidental read. It creates a practical local data-disclosure channel. ### Attack Path 1. The attacker supplies a valid fund workbook as the first argument. 2. The attacker supplies the path of a readable local text file as the second argument, such as a configuration, log, environment, or credential file. 3. `open(news_file, 'r', encoding='utf-8')` reads the file under the privileges of the Agent process. 4. Every nonempty line is s ...[truncated 990 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the market-news file feature if it is not required by the declared Skill functionality. 2. If the feature is required, explicitly document it and restrict it to user-provided files in an approved upload directory. 3. Canonicalize the path before use: ```python from pathlib import Path ALLOWED_INPUT_ROOT = Path("/approved/workspace/uploads").resolve() def validate_news_file(file_path: str) -> Path: candidate = Path(file_path) if candidate.is_symlink(): raise ValueError("Symbolic links are not permitted") resolved = candidate.resolve(strict=True) if resolved.suffix.lower() not in {".txt", ".md"}: raise ValueError("Only approved text file types are permitted") if resolved != ALLOWED_INPUT_ROOT and ALLOWED_INPUT_ROOT not in resolved.parents: raise ValueError("File is outside the approved input directory") if resolved.stat().st_size > 1_000_000: raise ValueError("Input file exceeds the size limit") return resolved ``` 4. Reject sensitive directories and symbolic links as defense-in-depth, but do not rely on a substring denylist as the primary boundary. 5. Apply a strict size limit and handle encoding errors safely. 6. Ensure that sensitive local content cannot be inserted into generated reports without explicit user authorization. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/read_excel.py:13
Finding
Inconsistent Path Validation Allows Reads Outside the Declared Input Boundary<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/read_excel.py:13-29` - `scripts/generate_weekly_report.py:19-31` - `scripts/generate_weekly_report_v2.py:19-29` - `scripts/generate_from_template.py:105` - `scripts/generate_from_template.py:266-284` - `scripts/generate_weekly_report_v3.py:28-53` **Vulnerability Type**: Missing and incomplete input-path authorization **Risk Level**: Medium ### Vulnerable Code The shared Excel reader accepts an unrestricted path: ```python def read_fund_excel(file_path: str) -> Dict[str, pd.DataFrame]: """ Read the fund weekly return Excel file. Parameters: file_path: Excel file path Returns: A dictionary whose keys are sheet names and values are DataFrames. """ excel_file = pd.ExcelFile(file_path) data = {} for sheet_name in excel_file.sheet_names: df = pd.read_excel(file_path, sheet_name=sheet_name) data[sheet_name] = df return data ``` The original weekly report generator has the same issue for both inputs: ```python def read_excel_data(fund_file: str, etf_file: str) -> Tuple[Dict[str, pd.DataFrame], Dict[str, Any]]: """Read Excel data.""" fund_data = {} fund_excel = pd.ExcelFile(fund_file) for sheet in fund_excel.sheet_names: fund_data[sheet] = pd.read_excel(fund_file, sheet_name=sheet) etf_data = {} etf_excel = pd.ExcelFile(etf_file) for sheet in etf_excel.sheet_names: etf_data[sheet] = pd.read_excel(etf_file, sheet_name=sheet) return fund_data, etf_data ``` The template generator also reads a separate Word file without an authorization check: ```python # Read template doc = Document(template_path) ``` Its command-line path handling delegates Excel access to the unrestricted shared reader: ```python template_path = sys.argv[1] excel_path = sys.argv[2] etf_path = sys.argv[3] if len(sys.argv) > 3 else None # Read data excel_data = read_fund_excel(excel_path) etf_data = extract_etf_ ...[truncated 3338 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create one centralized path-validation function and require every executable entry point to use it. 2. Replace `os.path.abspath()` with canonical resolution using `Path.resolve(strict=True)`. 3. Enforce containment within a configured upload or workspace root: ```python from pathlib import Path ALLOWED_INPUT_ROOT = Path("/approved/workspace/uploads").resolve() def validate_input(path_value: str, allowed_suffixes: set[str]) -> Path: candidate = Path(path_value) if candidate.is_symlink(): raise ValueError("Symbolic links are not permitted") resolved = candidate.resolve(strict=True) if resolved.suffix.lower() not in allowed_suffixes: raise ValueError("Unsupported file extension") if resolved != ALLOWED_INPUT_ROOT and ALLOWED_INPUT_ROOT not in resolved.parents: raise ValueError("Input is outside the approved workspace") if not resolved.is_file(): raise ValueError("Input must be a regular file") return resolved ``` 4. Validate actual file signatures in addition to filename extensions. 5. Apply size, sheet-count, row-count, and decompression limits to untrusted Office files. 6. Validate the fund workbook, optional ETF workbook, and Word template independently. 7. Remove legacy generators if they are obsolete; otherwise, apply the same validation to each. 8. Update `SKILL.md` to disclose Word-template and text-news inputs if those features are intentionally retained. 9. Treat sensitive-path denylisting only as defense-in-depth, not as the primary access-control mechanism. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_weekly_report_v3.py:1668
Finding
Unrestricted Output Paths Permit Overwriting Arbitrary Writable Files<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/generate_weekly_report.py:767-780` - `scripts/generate_weekly_report_v2.py:680-693` - `scripts/generate_weekly_report_v3.py:1668-1685` - `scripts/generate_from_template.py:252-257` - `scripts/generate_report.py:672-677` **Vulnerability Type**: Arbitrary writable-file overwrite **Risk Level**: Medium ### Vulnerable Code V3 accepts an unrestricted output argument and saves directly to it: ```python # Save document doc.save(output_file) print(f"Report generated: {output_file}") if __name__ == '__main__': import sys if len(sys.argv) < 2: print("Usage: python generate_weekly_report_v3.py <fund_excel> [etf_excel] [output_file]") sys.exit(1) fund_file = sys.argv[1] etf_file = sys.argv[2] if len(sys.argv) > 2 and sys.argv[2].endswith('.xlsx') else None output_file = sys.argv[3] if len(sys.argv) > 3 else f'fund_report_{datetime.now().strftime("%Y%m%d")}.docx' # If there is only one additional argument and it is not Excel, # treat it as the output filename. if len(sys.argv) == 3 and not sys.argv[2].endswith('.xlsx'): output_file = sys.argv[2] etf_file = None generate_report(fund_file, etf_file, output_file) ``` The original generator has equivalent behavior: ```python # Save document doc.save(output_file) if __name__ == '__main__': import sys if len(sys.argv) < 3: print("Usage: python generate_weekly_report.py <fund_excel> <etf_excel> [output_file]") sys.exit(1) fund_file = sys.argv[1] etf_file = sys.argv[2] output_file = sys.argv[3] if len(sys.argv) > 3 else 'fund_weekly_report.docx' ``` The template generator and enhanced report generator also save without validating the target: ```python if output_path is None: output_path = f'/tmp/fund_report_{start_date}-{end_date}.docx' doc.save(output_path) ``` ### Technical Analysis The generators do not enforce the documented requirement t ...[truncated 2136 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a dedicated output directory and reject destinations outside it. 2. Canonically resolve both the output directory and the requested parent directory. 3. Require the final filename to use the `.docx` extension. 4. Reject symbolic links in every component controlled by the caller. 5. Do not overwrite existing files by default. 6. Generate a server-controlled filename where possible. 7. Write to a securely created temporary file inside the approved output directory and atomically rename it after successful generation. Example hardening logic: ```python from pathlib import Path import os import tempfile OUTPUT_ROOT = Path("/approved/workspace/reports").resolve() def validate_output_path(file_name: str) -> Path: if Path(file_name).name != file_name: raise ValueError("Only an output filename may be supplied") if not file_name.lower().endswith(".docx"): raise ValueError("Output must use the .docx extension") target = OUTPUT_ROOT / file_name if target.exists() or target.is_symlink(): raise FileExistsError("Refusing to overwrite an existing output") resolved_parent = target.parent.resolve(strict=True) if resolved_parent != OUTPUT_ROOT: raise ValueError("Output is outside the approved directory") return target def save_report_safely(doc, target: Path) -> None: fd, temporary_name = tempfile.mkstemp( dir=str(OUTPUT_ROOT), prefix=".report-", suffix=".docx" ) os.close(fd) temporary_path = Path(temporary_name) try: doc.save(temporary_path) os.link(temporary_path, target) finally: temporary_path.unlink(missing_ok=True) ``` 8. Apply this policy consistently to all report generators rather than only the newest entry point. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
A description-behavior mismatch is a security concern because users and reviewers may approve the skill based on incomplete or inaccurate claims about what it does. In this case, undeclared ETF flow analysis and missing or partial implementations reduce transparency and can hide unexpected data handling, which increases the chance of misuse or unsafe trust assumptions.

Credential Access

High
Category
Privilege Escalation
Content
# 敏感目录黑名单
SENSITIVE_DIRS = [
    '/etc/', '/root/', '/home/',
    '.ssh', '.gnupg', '.config', '.aws', '.env',
    'id_rsa', 'id_ed25519', 'credentials'
]
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill declares security expectations in prose, but it does not define enforceable tool scope such as explicit permissions or allowed tools. That means an agent runtime may still grant broader file-read capability than intended, creating a gap between documented restrictions and actual execution controls.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The manifest description and usage instructions require users to read Chinese, and the file does not provide an opt-in language choice or explain that the skill is intentionally limited to a Chinese-speaking or region-specific context. This can violate language/locale policy when skills force a specific language without user choice.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The title and opening description frame the entire skill as producing chapter text in Chinese, and the document consistently defines fixed Chinese phrasing for all generated sections. There is no indication that the user may choose another language or locale, which can violate a language/locale policy when such constraints are imposed without opt-in.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The file title and the document body are entirely written in Chinese, presenting the skill content in a single language with no indication that users may choose another language or locale. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the constraint is explicitly justified.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This markdown file consists entirely of Chinese-language instructions and terminology, with no indication that users may choose another language or that the skill is intended only for a Chinese-speaking or region-specific context. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
该模板在无证据支撑的情况下,将ETF大额赎回解释为“机构端为平抑市场过热情绪、助力市场降温”的逆周期调节行为,属于带有目的性和政策含义的主观推断。若自动写入周报,可能生成误导性市场解读,放大不当立场性表述,并带来合规、声誉及投资者误判风险。

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The title and description explicitly define a Chinese-language wording template for fund weekly reports and present it as the required style. This imposes a specific language/locale on generated output without any opt-in, alternative language option, or stated region-specific compliance justification.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file is entirely written as prescriptive output guidance in Chinese, including wording rules and example phrasing, but nowhere indicates that Chinese is optional or limited to a China-specific deployment context. That creates a natural-language locale policy concern because the skill appears to force a specific language without user opt-in.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The title and the entire document are written as a prescriptive Chinese-language template, indicating the skill's output is expected in Chinese. There is no visible user opt-in, language selection, or documented region-specific justification for forcing this locale, which violates the language/locale choice policy.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This Python file contains natural-language strings and documentation that hard-code the skill to generate a Chinese-language weekly fund report. The policy for all file types says to flag language or locale constraints when the skill forces a specific language without user choice or a clearly justified documented constraint.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This Python file contains natural-language strings and documentation indicating the report is generated in Chinese, and the rest of the script hardcodes Chinese headings, labels, and output text. The file does not offer a user language/locale choice or explain that the skill is intentionally restricted to a Chinese-language or region-specific workflow.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This code file contains user-facing natural language in the module docstring and later CLI/status text that assumes Chinese as the only language. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This Python file contains natural-language instructions and hard-coded report content entirely in Chinese, beginning with the module description and continuing through generated headings and output strings. Because the skill does not offer a language/locale option or explain that it is intentionally region-specific, it appears to force a specific language without user opt-in.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script saves the generated Word document to a user-controlled path without validating the destination, restricting write locations, or warning before overwrite. In an agent context, this can overwrite arbitrary files the process can access, causing data loss or unintended file placement, especially if the output path is influenced by untrusted input.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code file contains natural-language docstrings and messages that assume a specific language/locale for all users. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly justified.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This markdown file presents all usage instructions and examples exclusively in Chinese, which can amount to a language/locale policy issue when no user opt-in or documented locale limitation is provided. The file does not indicate that the skill is intentionally China-specific or that users may choose another language.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
python-docx>=0.8.11
openpyxl>=3.0.0
Confidence
95% confidence
Finding
The dependency is specified with only a lower bound, which allows future installs to resolve to different versions over time. This creates supply-chain and reproducibility risk because a later vulnerable or incompatible release could be installed without review.

Unverifiable Dependency: pandas has 1 known advisory(ies) (CVE-2020-13091 (** DISPUTED ** pandas through 1.0.3 can unserialize and execute commands from an)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
python-docx>=0.8.11
openpyxl>=3.0.0
Confidence
95% confidence
Finding
The python-docx package is unpinned and may resolve to different versions in different environments. Unpinned dependencies weaken build reproducibility and can expose the skill to newly introduced vulnerable releases or breaking behavior.

Unverifiable Dependency: python-docx has 2 known advisory(ies) (CVE-2016-5851 (Improper Restriction of XML External Entity Reference in python-docx); CVE-2016-5851 (python-docx before 0.8.6 allows context-dependent attackers to conduct XML Exter)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
pandas>=1.3.0
python-docx>=0.8.11
openpyxl>=3.0.0
Confidence
95% confidence
Finding
Using openpyxl with only a minimum version leaves the final installed version uncontrolled. This is a common dependency hygiene issue that can increase exposure to supply-chain problems and make it hard to verify whether deployments are patched.

Unverifiable Dependency: openpyxl has 2 known advisory(ies) (CVE-2017-5992 (Improper Restriction of XML External Entity Reference in Openpyxl); CVE-2017-5992 (Openpyxl 2.4.1 resolves external entities by default, which allows remote attack)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Static analysis

No suspicious patterns detected.