Back to skill

Security audit

InvAssistant

Security checks for vulnerabilities and agentic risk

Overview

This investment assistant is mostly purpose-aligned, but it needs Review because it asks to persist portfolio rules into agent memory/automation prompts and can send sensitive portfolio reports to arbitrary webhooks.

Install only if you are comfortable reviewing its state changes before they are applied. Keep webhook push disabled unless you verify the destination, use HTTPS official provider URLs, and redact sensitive portfolio details. Treat all generated entry/exit signals as advisory and manually verify market data, cost basis, and risk checks before acting.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T02 · Agent Memory Poisoning

Error
Location
references/candidate_admission_gates.md:74
Finding
Persistent modification of Agent memory and automation prompts<![CDATA[ ## Vulnerability Details **File Location**: `references/candidate_admission_gates.md:74-83` **Vulnerability Type**: Persistent Agent state manipulation **Risk Level**: High ### Evidence The following is a faithful English rendering of the source instructions: ```markdown After producing the decision, synchronization is mandatory: - instrument-master.yaml — add gate_verdicts / s6_status / lookthrough / eligibility - asset-class-map.yaml — candidate_pool section - a-share-capital-plan.yaml — look-through block, S6 threshold table, open_items - Pre-market/post-market automation prompts — add the candidate-monitoring template and a hard constraint prohibiting candidates from entering P0 - runtime-memory.md and the current day's memory log Account-level parameter layer: May be written directly to runtime files and take effect immediately. ``` ### Technical Analysis The candidate-admission workflow does not stop after producing a portfolio-analysis result. It requires the Agent to update pre-market and post-market automation prompts, `runtime-memory.md`, daily memory logs, and other runtime state files. Changes to automation prompts and general runtime memory affect future Agent sessions. This is broader than maintaining a Skill-local portfolio configuration and exceeds the minimum privileges necessary to assess whether a candidate qualifies for a watchlist. The instruction is particularly risky because it uses mandatory language and states that certain changes may take effect immediately. There is no requirement for separate user approval, no restriction to a Skill-owned state namespace, and no rollback or integrity mechanism. ### Attack Path 1. A user invokes the Skill for a candidate-admission evaluation. 2. The Agent loads `references/candidate_admission_gates.md`. 3. The evaluation produces a decision. 4. The mandatory deployment checklist directs the Agent to edit automation prompts, runtime memory, and the current daily memory log. 5. The ...[truncated 816 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove mandatory writes to general Agent prompts and memory from the candidate-admission workflow. 2. Return proposed state changes as a patch or structured plan instead of applying them automatically. 3. Require explicit, per-operation user confirmation before modifying: - Pre-market automation prompts - Post-market automation prompts - `runtime-memory.md` - Daily memory logs 4. Store necessary portfolio state only in a dedicated Skill-owned file with a documented schema. 5. Prevent Skill-owned state from containing instructions that alter general Agent safety constraints or unrelated future tasks. 6. Record the requesting user, timestamp, old value, new value, and reason for every approved persistent change. 7. Provide rollback support and integrity checks for approved state updates. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/portfolio_checker.py:688
Finding
Unrestricted webhook destinations can receive sensitive portfolio reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/portfolio_checker.py:688-715`, `scripts/send_wecom.py:39-76`, `scripts/send_dingtalk.py:48-97`, `scripts/send_feishu.py:47-161`, `scripts/exit_engine.py:84-133` **Vulnerability Type**: Arbitrary outbound destination and sensitive financial-data disclosure **Risk Level**: High ### Evidence The main checker forwards generated reports to URLs taken directly from configuration: ```python def do_push(config, report_text): adapters = config.get("adapters", {}) pushed = False if adapters.get("wechatwork", {}).get("enabled"): try: from send_wecom import push_signal push_signal(report_text, adapters["wechatwork"].get("webhook_url", "")) pushed = True except Exception as e: print(f"[push failed] {e}", file=sys.stderr) if adapters.get("dingtalk", {}).get("enabled"): try: from send_dingtalk import push_signal push_signal( report_text, adapters["dingtalk"].get("webhook_url", ""), adapters["dingtalk"].get("secret", "") ) pushed = True except Exception as e: print(f"[push failed] {e}", file=sys.stderr) if adapters.get("feishu", {}).get("enabled"): try: from send_feishu import push_signal push_signal( report_text, adapters["feishu"].get("webhook_url", ""), adapters["feishu"].get("secret", "") ) pushed = True except Exception as e: print(f"[push failed] {e}", file=sys.stderr) ``` Each sender uses the supplied destination without scheme or host validation. For example: ```python def send_markdown(webhook_url, content): payload = {"msgtype": "markdown", "markdown": {"content": content}} data = json.dumps(payload, ensure_ascii=False).encode("utf-8") req = urllib.request.Req ...[truncated 3176 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https://` for every webhook. 2. Allowlist official webhook hostnames separately for WeCom, DingTalk, and Feishu. 3. Resolve destination DNS and reject loopback, private, link-local, multicast, and reserved IP ranges. 4. Disable automatic redirects or revalidate every redirect target against the same scheme, hostname, and address rules. 5. Reject URLs containing user information, unexpected ports, fragments, or unsupported schemes. 6. Display the normalized destination hostname and the categories of data to be sent before the first transmission. 7. Redact cost basis, position size, account identifiers, and detailed action data by default. 8. Add a separate explicit option for transmitting sensitive fields. 9. Prefer environment variables or an operating-system secret store for webhook tokens and signing secrets instead of plaintext JSON. 10. Apply restrictive filesystem permissions to local configuration and generated report files. 11. Add timeouts and response-size limits to all `urlopen()` calls. 12. Add tests covering HTTP rejection, hostile redirects, DNS rebinding scenarios, and unapproved hosts. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/redline_engine.py:204
Finding
Market-risk admission gate fails open when benchmark history is insufficient<![CDATA[ ## Vulnerability Details **File Location**: `scripts/redline_engine.py:204-238` **Vulnerability Type**: Fail-open financial risk validation **Risk Level**: High ### Evidence ```python results = [] checks_passed = 0 # QQQ check qqq_returns = qqq["Close"].pct_change().dropna() if len(qqq_returns) >= 3: if not all(r < 0 for r in qqq_returns.tail(3)): checks_passed += 1 results.append("QQQ check passed") else: results.append("QQQ declined for three consecutive days") else: checks_passed += 1 results.append("Insufficient QQQ data; passed by default") # VIX check latest_vix = vix["Close"].iloc[-1] if latest_vix < vix_threshold: checks_passed += 1 results.append(f"VIX = {latest_vix:.2f}, below threshold") else: results.append(f"VIX = {latest_vix:.2f}, at or above threshold") # SPX check spx_returns = spx["Close"].pct_change().dropna() if len(spx_returns) >= 3: if not all(r < 0 for r in spx_returns.tail(3)): checks_passed += 1 results.append("SPX check passed") else: results.append("SPX declined for three consecutive days") else: checks_passed += 1 results.append("Insufficient SPX data; passed by default") passed = checks_passed >= 3 return passed, "; ".join(results) ``` ### Technical Analysis `check_market()` correctly rejects a completely missing QQQ, SPX, or VIX dataset. However, if QQQ or SPX exists but contains fewer than three usable returns, the function increments `checks_passed` and records the check as passing by default. This creates inconsistent safety semantics: - Missing dataset: fail closed - Present but insufficient dataset: fail open The final result passes when all three counters succeed. Consequently, truncated benchmark datasets plus a low VIX value can satisfy the market-risk red line even though QQQ and SPX risk were not assessed. This function feeds `run_redline_check()`, where all three red lines can produce an entry action. The defec ...[truncated 1184 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat insufficient QQQ or SPX history as `unknown` or failed, never passed. 2. Block entry decisions whenever a required market-risk input is missing, stale, malformed, or too short. 3. Return a structured three-state result such as `pass`, `fail`, or `unavailable`. 4. Require at least four valid ordered closing prices to calculate three daily returns. 5. Validate timestamps, duplicate rows, non-finite values, and data freshness before evaluating risk. 6. Show a prominent “assessment unavailable” result instead of a positive check mark. 7. Add tests for: - Null datasets - Empty datasets - One to three prices - NaN-heavy data - Stale data - Valid three-day decline - Valid non-decline 8. Prevent `run_redline_check()` from generating an entry action if any required check is unavailable. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Non-reproducible and incomplete dependency specification<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1`, `README.md:17-21`, `scripts/data_fetcher.py:8-9` **Vulnerability Type**: Unsafe dependency resolution and undeclared runtime dependency **Risk Level**: Medium ### Evidence The documented installation command executes dependency resolution: ```bash cp -r invassistant-skill ~/.workbuddy/skills/invassistant pip install -r requirements.txt ``` The dependency file contains only an open-ended minimum version: ```text pandas>=1.5.0 ``` The runtime imports another third-party package that is not declared: ```python import requests import pandas as pd ``` ### Technical Analysis Using `pandas>=1.5.0` permits pip to install any later version accepted by the environment. There is no lockfile, upper bound, reviewed exact version, or integrity hash. As a result, two installations at different times can execute different third-party code even though the Skill package itself has not changed. The code also imports `requests`, but `requirements.txt` does not declare it. This makes successful execution depend on an ambient package installation. The imported version and source are therefore outside the project’s declared dependency controls. No typosquatted or known-malicious package was identified in the supplied dependency file. The confirmed defect is the absence of reproducible, complete dependency controls rather than evidence that the current package names are malicious. ### Attack Path 1. A user follows the README and runs `pip install -r requirements.txt`. 2. Pip resolves a current version of pandas rather than a version audited with the Skill. 3. Runtime behavior varies with installation date, package index, transitive dependency resolution, and local environment. 4. The Skill later imports `requests` from the ambient Python environment. 5. If the environment contains an incompatible, compromised, or locally shadowed package, that code executes when `data_fetcher.py` is imported or use ...[truncated 531 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Declare every direct third-party dependency, including `requests`. 2. Pin dependencies to exact reviewed versions. 3. Generate a lockfile containing resolved transitive versions. 4. Use package hashes, such as pip `--require-hashes`, to verify artifact integrity. 5. Document the trusted package index and avoid untrusted extra indexes. 6. Test supported Python versions against the locked dependency set. 7. Add automated dependency-vulnerability scanning and controlled update review. 8. Use an isolated virtual environment rather than relying on ambient packages. 9. Consider replacing `requests` with the standard library if minimizing dependencies is a project objective, but retain explicit TLS, timeout, and response-size controls. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (36)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a substantive investment portfolio management system with multiple analytical and decision-making capabilities. The actual supplied code chunk is effectively empty aside from a package marker comment, so it does not implement the stated functionality. This is a material description-behavior mismatch because the code has no meaningful behavior aligned with the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a full investment portfolio management and risk-governance framework, including asset-class-specific rules, risk red lines, quality scoring, and disciplined trading logic. The supplied code does not implement any of those portfolio-management behaviors. Instead, it is a narrow support module for downloading historical market data from Yahoo Finance using HTTP requests, parsing JSON into pandas DataFrames, and handling retries and rate limits. While data fetching could be a supporting component of an investment system, this chunk’s actual primary purpose is data acquisition, not portfolio management. Therefore the description materially overstates and misrepresents the behavior of the provided code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description promises a broad portfolio management system spanning multiple asset classes, market regions, quality scoring, and both entry/exit discipline. This code chunk is much narrower: it is specifically an exit engine. It checks single-position exit conditions (take profit, stop loss, trend break, momentum fade) and a portfolio-level systemic risk condition using US market indicators (^VIX, QQQ, ^GSPC). While exit logic and risk control are related to the declared domain, the implemented functionality does not substantiate the major claimed components such as A/B/C differentiated asset rules, seven portfolio red lines, four-factor QMS scoring, or a complete multi-market portfolio management framework. Therefore the description materially overstates and misrepresents what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a substantive investment-management framework with defined analytical/risk capabilities and regional market coverage. The supplied code chunk does not implement those behaviors; it merely writes a default configuration file (`invassistant-config.json`). While the config contains sample fields related to watchlists, strategy names, exit parameters, systemic risk thresholds, and enterprise chat webhook settings, these are data placeholders rather than operational portfolio-management logic. The code also introduces bot/webhook adapter and command-trigger configuration, which is not part of the declared purpose. This is a material description-to-behavior mismatch because the actual code’s primary purpose is configuration initialization, not execution of the described investment framework.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
The code is broadly related to portfolio management and risk/signal checking, so the domain matches. However, the declared description overstates and materially differs from the behavior visible in this code chunk. The implementation centers on a portfolio checker CLI with configurable watchlists, market data fetching, entry/exit signal generation, systemic risk checks, report formatting, JSON export, and optional push notifications. Key declared features—especially 7 red-line portfolio risk controls and 4-factor QMS quality scoring—are not implemented or evidenced in this chunk. Additionally, the code has undeclared outbound notification and local report persistence capabilities. Therefore this is a meaningful description-behavior mismatch rather than a mere omission of minor details.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The description overstates and mischaracterizes the code. The code is narrowly focused on stock entry evaluation and market-condition gating, not a broader multi-asset portfolio management framework. Its 'A/B' terminology refers to two entry modes, which differs materially from the declared A/B/C asset-class rule system. The code implements 3 red lines for entry plus a 4-condition trend mode, but not 7 portfolio risk red lines or a 4-factor QMS scoring system. It also lacks portfolio-level controls, position review, and explicit exit management. While the investment-related domain is aligned at a high level, the concrete functionality is substantially different from the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a portfolio management and investment decision framework centered on asset rules, risk controls, and scoring logic. The supplied code does not implement portfolio management logic, risk-redline analysis, quality scoring, or entry/exit decision engines. Instead, it is an integration/notification script whose primary purpose is to send already-produced results to DingTalk via webhook. While the message content concerns portfolio signal checks, the code chunk itself is only a delivery adapter, with undeclared external network messaging capability and configuration handling for webhooks/secrets. Therefore the code's actual behavior is materially different from the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is for an investment portfolio management/risk-control framework with asset rules and scoring logic. The supplied code chunk does not implement portfolio analysis, risk red-line checks, asset-class rules, or quality scoring. Instead, it is a messaging adapter/script whose primary purpose is to deliver already-produced results to Feishu via webhook. While the messages reference portfolio signal checks, this code is only a notification/presentation component, which is materially different from the declared core functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a portfolio management/risk-control framework with asset allocation rules, red-line controls, and quality scoring logic. The supplied code does not implement portfolio management, scoring, trading rules, or market-specific investment logic. Instead, it is a messaging adapter: it retrieves a WeCom webhook URL, reads an existing JSON report, formats it into Markdown, and sends it over the network to Enterprise WeCom. While the message content references investment signal checks, this script’s actual primary purpose is notification delivery, which is materially different from the declared portfolio-management framework. Therefore this code chunk is a clear description-behavior mismatch.

Lp1

High
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The module performs outbound HTTP requests to Yahoo Finance via requests.get, which is a real network capability. If the skill does not explicitly declare or constrain network permissions, users and hosting platforms may be unaware that execution sends data to an external service, creating a trust, privacy, and policy-enforcement gap. In an investment assistant, network access is contextually expected, but undeclared capability still increases risk because fetched symbols or usage patterns may be externally observable and remote content can influence downstream decisions.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'file_write' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'env' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'file_read' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README explicitly advertises `send_*.py` scripts for WeChat/DingTalk/Feishu push delivery but provides no warning that portfolio holdings, balances, or possibly host metadata may be transmitted to third-party messaging platforms. In an investment-management skill, this omission is security-relevant because users may deploy notification integrations without understanding the privacy, retention, and exfiltration implications of sending sensitive financial data off-platform.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
该 markdown 文件描述了一个“多市场投资策略管理框架”,并在快速开始中直接引导用户执行“检查持仓”,但未提供任何关于投资风险、结果仅供参考、或不应替代专业建议的用户警示。对于可能影响用户资产与财务决策的技能说明,缺少此类披露会降低用户对风险后果的认知。

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
The file presents itself bilingually, but the required output format is specified only in English and uses fixed English labels such as 'Structured review report', 'Action list', and 'Data-source annotations'. Elsewhere, the document also mandates English-coded constructs like state names and labels, without explicitly offering the user a language choice for responses, which can violate a language/locale policy requiring opt-in.

Natural-Language Policy Violations

Medium
Confidence
82% confidence
Finding
The entire skill is written in Chinese and does not indicate that users may interact in another language or choose their preferred locale. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation unless the locale restriction is explicitly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This markdown file presents the skill content entirely in Chinese, including headings, rules, and operational instructions, but does not state that the skill is region-specific or provide any user language opt-in. Under the policy, forcing a specific language without user choice is a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The module title, description, and all user-facing explanatory strings are written exclusively in Chinese, indicating a fixed language/locale behavior. There is no indication that the user can opt into another language or that the locale restriction is required for a region-specific compliance purpose.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The module docstring presents the skill name, description, and usage entirely in Chinese, and the script's user-facing output is likewise Chinese-only. This imposes a specific language/locale on users without any opt-in or documented region-specific justification, which matches the language policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This file presents its docstring, CLI help context, warnings, and operational output in Chinese, and there is no indication that users can opt into another language. That creates a locale policy concern under the language-choice rule for natural-language content in code files.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The skill can send outbound messages to WeCom, DingTalk, and Feishu webhooks, which extends it from local analysis into external data exfiltration/notification. Because reports may include portfolio positions, trading signals, and market-risk summaries, enabling push can leak sensitive financial information to third-party endpoints if configuration is malicious or mistaken.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The code contradicts its own documented control requirement by always treating valuation as passed in Mode B, even though the strategy states all four conditions must be satisfied. In an investment decision engine, this weakens a gating risk control and can systematically admit overvalued positions that the user believes are being filtered out.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The documented breakout rule requires both a price breakout and a volume surge, but the implementation marks breakout as passed even when volume is insufficient. This creates a silent control bypass where users may enter weaker breakouts under the false assumption that liquidity/confirmation checks were enforced.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The Feishu post payload is always placed under the "zh_cn" locale key, and the script’s user-facing strings are entirely Chinese. This forces a specific language/locale for the skill’s output without any opt-in, fallback, or explanation that the tool is intentionally limited to a Chinese-only environment.

Static analysis

No suspicious patterns detected.