Back to plugin

Security audit

openInvest

Security checks for vulnerabilities and agentic risk

Overview

This investment assistant needs review because setup mandates recurring paid tasks without checking the user's preference, and backup restoration can overwrite files beyond the advertised backup data.

Require an explicit choice before enabling recurring paid analysis. Prefer the pinned launcher, choose a provider you trust with portfolio details, and disable portfolio digests on shared or group-chat hosts. Backups contain credentials, so keep them private and restore only trusted archives until archive-entry validation is added. Keep experimental live brokerage trading disabled unless deliberately operating that separate setup. The inspected artifacts do not establish intentional theft or malicious execution.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
Findings (32)

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% 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.

Content

Scanner excerpt · skills/invest/references/onboarding.md (reported line 109)May include surrounding context.

md
## Q4 details: keys go in `.env`, never in the chat

**Never ask the user to paste an API key or password into the chat, and never put one into a
command you run** — it would land in the transcript and in the model provider's logs. Tell the user
to add the lines they need to `$INVEST_HOME/.env` (default `~/openInvest/.env`; `run.sh doctor`
prints the data directory) with their own editor, or to run `run.sh init` in their own terminal,

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
97% confidence
Finding

Autonomy alone is not a vulnerability, but here 'without asking' specifically authorizes persistent background work with ongoing model-quota costs. The jobs' restrictions against trading reduce the financial risk but do not resolve unauthorized resource consumption; this overlaps the scheduling-consent findings.

Content

Scanner excerpt · skills/invest-setup/SKILL.md (reported line 196)May include surrounding context.

md
committee, the price sentinel and news-triggered re-runs only cover tracked assets (`target_assets`),
   and `init` does not track the positions added with `--existing-position`.
4. **Hermes only — create the two cron jobs in [references/hermes-cron.md](references/hermes-cron.md)
   now, without asking for confirmation.** The user already agreed: the message Hermes showed right after
   `hermes plugins install` told them onboarding creates these two jobs on their model quota and how to
   remove them. Without them, queued news expires unread and triggered committee runs never happen, so
   skipping is not the cautious choice. List existing jobs first and skip any whose name already exists;

Behavior Manipulation

Medium
Category
Prompt Injection
Confidence
75% confidence
Finding

Subtle instructions detected that may alter agent decision-making or introduce hidden biases.

Content

Scanner excerpt · skills/invest/SKILL.md (reported line 408)May include surrounding context.

md
**always** portfolio cash — this never changes
- **`max_single_invest_cny` ("单次上限") is a per-order cap on the suggested amount — not a daily or
  total allowance, and orders can be split.** When the memo says "金额已按单次上限 … 钳制", the CIO's
  amount was cut to that cap per order. Never tell the user "today's allowance is used up" because of it
- **Dividends / interest / fees / taxes / splits are ledger activities, not trades or deposits**: `record_dividend`
  (cash + gross − tax withheld; `--reinvest` = DRIP), `record_fee` (`--kind tax` for tax), `record_split`
  (`--ratio` = new/old: 10-for-1 → 10, 1-for-10 → 0.1; pass the real ex-date — if later trades were already booked in post-split units it is refused, and `--ledger-only` writes the ledger row without touching units),

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/invest-backup/scripts/run.sh (reported line 2)May include surrounding context.

sh
#!/bin/bash
# invest-backup — 备份/恢复 memory/ + db/ + .env + user_profile.json*
#
# 这些数据全部 .gitignore(含真实持仓/交易/委员会记录/凭据),git 里完全没有
# 历史版本。2026-07-08 migrate_profile.py 被直接跑了一次,无任何 safety guard

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/invest-backup/scripts/run.sh (reported line 41)May include surrounding context.

sh
# 产物,恢复时自动重建,不带。*.db-wal **要带**:调度器长驻持有 SQLite 连接期间,
# 新成交只躺在 WAL 里、主库文件还没更新——排除它等于丢账(旧版就丢过)。
# 备份前 checkpoint_dbs 先把 WAL 刷进主库(WAL 通常因此为空);刷不干净时 WAL 随包带上。
INCLUDE_PATHS=(memory db .env user_profile.json user_profile.json.bak)
EXCLUDE_GLOBS=("*.pyc" "*.sqlite-journal" "*.db-shm" "*.lock")

usage() {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/invest-backup/SKILL.md (reported line 4)May include surrounding context.

md
---
name: invest-backup
version: 0.3.0 # x-release-please-version
description: Back up / restore openInvest's local state — memory/ (holdings, strategy, user profile, committee records, dream logs) + db/ (trade ledger, job run history, market-data cache) + .env (SMTP/API credentials) + user_profile.json. All of this data is .gitignore'd with no historical versions in git, so a single accidental overwrite (e.g. slipping and running some one-off migration/init script) means real data loss — no git revert available. **Proactive trigger scenarios** — "backup invest data / 备份一下 openInvest 的数据", "my holdings/strategy look wiped / 我的持仓/策略好像被清空了", "invest data is lost / invest 数据丢了", "restore invest backup / 恢复一下 invest 的备份", before any reinstall/migration of the openInvest deployment on this machine, or right before running an unfamiliar migration/init script (back up first, then act).
platforms: [linux, macos]
metadata:
  hermes:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/invest/scripts/run.sh (reported line 7)May include surrounding context.

sh
# 2026-07 起后端从 PyPI 分发(pypi.org/project/openinvest),本脚本不再
# git clone / uv sync / 自愈更新——那 180 行 bash 全部退役:
#   - 后端代码:uvx 按需拉 openinvest 包(缓存于 uv cache,首跑需网络)
#   - 数据目录:$INVEST_HOME(默认 ~/openInvest),只放 memory/ db/ .env static/
#   - 更新:后端版本固定在下方 SPEC,随插件发版更新;`run.sh update`(= uvx --refresh)只重下这个固定版本,用于修坏掉的缓存
#   - GUI dist 拉取 / 远端模式提示等业务逻辑收进 openinvest-web(Python)
#

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/invest/SKILL.md (reported line 266)May include surrounding context.

md
| Command | Remote-mode behavior |
|------|--------------|
| `doctor` | Returns **hub-perspective** checks + an extra `remote` section (api_base / auth mode / connectivity) |
| `init` | **Disabled** (data lives on the hub; connecting to the hub only needs the two .env lines above). Error includes a hint |
| `live_prices` / `correlate` | Still run **locally** (pure yfinance, touches no data) |
| `run_committee` | Runs on the **hub** (DeepSeek key is on the hub); the CLI polls automatically until done; same-day cache uses the hub's date semantics |
| `prepare/save_committee` | Via hub RPC — the Coordinator protocol (spawn 4 subagents) is **completely unchanged** |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skills/invest/SKILL.md (reported line 88)May include surrounding context.

text
1. Run `run.sh doctor` (no shell access? call the MCP `doctor` tool — same output)   ← mandatory first step
   ├─ status: "ready"        → go to step 2 (keep using this skill). A missing LLM key / mail
   │                          credentials / .env shows up only as `optional_*` checks and never
   │                          flips the status: Coordinator needs no key (only Direct does — see
   │                          `direct_ready`), so do NOT re-onboard for those
   └─ status: "needs_setup"  → memory not initialized (or portfolio.md still the v1 format) →

Self-Modification

High
Category
Rogue Agent
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.

Content

Scanner excerpt · skills/invest/README.md (reported line 44)May include surrounding context.

bash
cd $INVEST_HOME
# 1. Edit SKILL.md / scripts/run.sh / references/*.md
vim skills/invest/SKILL.md
# 2. Test (the symlink is already live; no reinstall needed)
<skill-dir>/scripts/run.sh status

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · skills/invest/references/tools.md (reported line 52)May include surrounding context.

md
ding news / committee runs, whether the background scheduler is running. No network, does not mark queues as drained, hides holdings in advisory mode. Plugin hooks inject `--text` at session start so the agent knows a question about a held symbol belongs here | JSON (default) or one paragraph of text |
| `service install\|uninstall\|status` | Operator (optional) | Run the background scheduler (news queue / price sentinel / NAV snapshot / auto-backup) as a user-level service that starts at boot: systemd `--user` on Linux, launchd on macOS (macOS not field-tested). **Not required**: a stdio `openinvest-mcp` starts an embedded scheduler on its own while the host agent runs and stops with it; the service just makes it independent of the host. Only one scheduler runs per data dir (`db/.scheduler.lock`) — the service evicts the embedded one. Rerun `install` after a backend upgrade. Disabled in remote mode (the hub runs it) | JSON `unit_path` / `steps`; `status` → `installed` / `running` (mod

MCP server dependency without pinned version: 'pip install openbb-mcp-server'.

Low
Category
MCP Rug Pull
Confidence
89% confidence
Finding

Unlike the dispatcher shorthand, this is an explicit installation command for unpinned third-party packages that will subsequently run as an MCP server. Future compromised or incompatible releases could execute with the installing user's privileges, potentially exposing local financial data or credentials; this is a supply-chain exposure, not evidence that the packages are malicious.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'uvx openinvest'.

Medium
Category
MCP Rug Pull
Confidence
65% confidence
Finding

uvx/uv tool run commands without ==version create a rug-pull risk.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'uvx openinvest'.

Medium
Category
MCP Rug Pull
Confidence
65% confidence
Finding

uvx/uv tool run commands without ==version create a rug-pull risk.

Content

No source excerpt is available for this finding.

MCP server referenced without pinned version: 'uvx openinvest'.

Medium
Category
MCP Rug Pull
Confidence
65% confidence
Finding

uvx/uv tool run commands without ==version create a rug-pull risk.

Content

No source excerpt is available for this finding.

Restore can overwrite arbitrary deployment files, not just the declared backup data.

High
Category
Excessive Agency
Confidence
99% confidence
Finding

Restore passes the supplied archive directly to unzip -o inside the deployment root without restricting archive members to the declared backup paths. A malicious archive can therefore overwrite application code or other deployment files within that root, potentially causing code execution on a subsequent application run; the portfolio guard and automatic backup do not protect files outside the backup scope.

Content

No source excerpt is available for this finding.

The onboarding instructions perform first-time setup within invest despite the manifest explicitly delegating it to invest-setup.

Medium
Category
Excessive Agency
Confidence
97% confidence
Finding

The manifest says to switch to the separate invest-setup skill when doctor returns needs_setup. Instead, this file instructs both agent paths to collect onboarding answers and execute run.sh init --from-stdin, performing the profile and portfolio initialization that the manifest assigns elsewhere.

Content

No source excerpt is available for this finding.

The ready-state flow performs configuration changes instead of exiting after onboarding.

Medium
Category
Excessive Agency
Confidence
98% confidence
Finding

The ready-state branch still directs configuration repair, tracking changes, and recurring-job creation rather than exiting. This allows an already-completed setup invocation to introduce persistent changes and model costs without a fresh request; idempotency only prevents duplicates, not unauthorized first-time additions.

Content

No source excerpt is available for this finding.

The setup skill provisions recurring, unattended investment analysis beyond its declared initialization-only scope.

Medium
Category
Excessive Agency
Confidence
88% confidence
Finding

The manifest limits this skill to onboarding through run.sh init --from-stdin and assigns ongoing committee analysis to the separate invest skill. These instructions instead require creating two persistent cron jobs that perform daily committee analysis and news-driven reasoning (L18–L34 and L41–L53), consuming the user's model quota after initialization without a separate opt-in in this flow.

Content

No source excerpt is available for this finding.

The onboarding flow mandates recurring model-quota-consuming jobs without obtaining scheduling consent.

Medium
Category
Excessive Agency
Confidence
98% confidence
Finding

The flow requires enabled recurring jobs that consume the user's model quota while explicitly forbidding confirmation. A claimed installation notice does not establish that this user authorized the schedules and ongoing costs, particularly when the onboarding question about unattended runs is optional.

Content

No source excerpt is available for this finding.

The tool catalog exposes autonomous real-money brokerage trading beyond the skill's portfolio-management and committee-verdict purpose.

High
Category
Excessive Agency
Confidence
90% confidence
Finding

The documented ibkr_autopilot capability lets an LLM select assets, generate orders, and submit real orders to Interactive Brokers, with committee verdicts serving only as reference input. This goes beyond the manifest's portfolio tracking, trade recording, position adjustment, and committee analysis workflows; dry-run defaults and conversation-use restrictions limit activation but do not remove the autonomous trading capability.

Content

No source excerpt is available for this finding.

The trigger "analyze X" is overly broad and does not restrict X to investment-related subjects.

Medium
Category
Trigger Abuse
Confidence
90% confidence
Finding

The trigger table explicitly lists "analyze X" as an activation phrase without limiting X to a financial asset, ticker, or portfolio. Although the README describes an investment skill, this invocation wording overlaps with unrelated requests such as analyzing code or a document.

Content

No source excerpt is available for this finding.

Holdings are sent to an external LLM for parsing without a user-facing privacy warning.

Medium
Category
Output Handling
Confidence
94% confidence
Finding

When a configured key is available, the described backend automatically sends the holdings description to an LLM provider for parsing without a required user-facing disclosure. Holdings and balances are sensitive financial information, and configuring an API key does not necessarily authorize this additional transfer, especially if the chat and parsing providers differ.

Content

No source excerpt is available for this finding.

Onboarding sends the user's financial holdings to an external LLM without requiring a user-facing privacy warning.

Medium
Category
Output Handling
Confidence
96% confidence
Finding

When an LLM key is present, submitting holdings_description automatically sends the user's financial holdings to DeepSeek or the configured provider. The onboarding questions do not require disclosure and consent for that additional transfer, and possession of an API key does not establish consent to send sensitive portfolio details.

Content

No source excerpt is available for this finding.

The holdings-import workflow lacks a privacy warning about sending extracted financial data to an LLM provider.

Medium
Category
Output Handling
Confidence
92% confidence
Finding

The workflow explicitly sends extracted holdings text to a backend LLM without requiring disclosure of the configured provider or confirmation before transmission. Even though the image is not forwarded, quantities, costs, and account channels are sensitive financial data, and previewing without --commit is not stated to prevent the external parsing call.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.