Back to skill

Security audit

Stock Copilot Pro

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed stock-analysis helper that uses QVeris data and local watchlist state, with some operational risks users should understand before enabling automation.

Install this only if you are comfortable sending stock symbols, watchlist context, and related analysis queries to QVeris using your QVERIS_API_KEY. Review auto-invocation and do not add the optional cron jobs unless you want recurring API usage. Use --no-evolution if you do not want local tool-template state, and treat outputs as research rather than investment advice.

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/lib/infra/qveris-client.mjs:115
Finding
Unbounded Secondary Full-Content Response Can Exhaust Process Memory## Vulnerability Details **File Location**: `scripts/lib/infra/qveris-client.mjs`, lines 115-124 **Vulnerability Type**: Unbounded network response buffering **Risk Level**: Medium ### Vulnerable Code ```js const res = await fetchImpl(checked.url, { signal }); if (!res.ok) { return { ...initial, meta: { ...initial.meta, fetchError: `full content fetch failed: ${res.status}`, }, }; } const fullContent = await res.json(); ``` ### Technical Analysis The secondary `full_content_file_url` retrieval parses the complete HTTP response through `res.json()` without enforcing a maximum body size. Although primary tool execution accepts a `max_response_size` value, that limit does not apply to this secondary request. The request has a timeout and validates that the URL uses HTTPS and belongs to `qveris.ai` or an allowed subdomain. These controls reduce SSRF exposure but do not limit the number of bytes buffered and parsed before the response is accepted. A compromised, malicious, or malfunctioning allowed endpoint can therefore return an extremely large JSON document. Node.js may buffer the body and allocate additional memory while decoding and parsing it, resulting in excessive resource consumption or process termination. ### Attack Path 1. A QVeris tool response contains malformed or unusable `truncated_content` and supplies a `full_content_file_url`. 2. The radar execution path calls `resolveToolPayload` with `fetchFullContent: true`. 3. URL validation accepts an HTTPS URL hosted on `qveris.ai` or an allowed subdomain. 4. The allowed endpoint returns a very large JSON response. 5. The client invokes `res.json()` and buffers/parses the response without a byte limit. 6. The Agent process experiences excessive memory consumption, event-loop disruption, or termination. Exploitation requires control or compromise of an allowed QVeris endpoint, or an operational failure that causes it to return an unexpectedly large payload. ### Impact ...[truncated 345 chars]
Remediation
## Remediation Suggestions 1. Stream the response body and enforce a strict maximum byte count before JSON parsing. 2. Reject responses whose valid `Content-Length` header exceeds the configured limit, while still enforcing the streaming limit because the header may be absent or false. 3. Abort the request immediately when the accumulated body exceeds the limit. 4. Reuse or derive the limit from the existing `max_response_size` setting and impose a conservative hard upper bound. 5. Validate the response `Content-Type` before parsing and reject non-JSON content. 6. Catch body-size and JSON-parsing failures and return a bounded, redacted error. 7. Add tests covering oversized chunked responses, misleading `Content-Length` values, missing length headers, invalid JSON, and requests aborted after crossing the byte limit. 8. Consider disabling secondary full-content retrieval by default unless the larger payload is necessary for the requested operation.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (59)

Ae1

High
Category
analysis-evasion
Content
- OpenClaw LLM-ready flow: structured data in code + guided narrative in `SKILL.md`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Primary script: `scripts/stock_copilot_pro.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
- Runtime evolution state stores tool metadata and parameter templates only.
- API keys and authorization headers are not persisted in evolution files.
- Evolution state is pruned with bounded size to avoid unbounded local persistence.
- Script calls only QVeris APIs (`qveris.ai`) and does not install packages or run arbitrary commands.
- Even when provider payload includes `full_content_file_url`, fetch is host-restricted to `qveris.ai` to prevent arbitrary outbound requests.

## Disclaimer
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill requests access to an environment secret (`QVERIS_API_KEY`) but does not declare a clear tool/permission scope such as `allowed-tools` or equivalent execution boundaries. This weakens least-privilege guarantees and makes it harder for the host to constrain what the auto-invoked skill can do with available capabilities if the implementation expands or is misrouted.

Vague Triggers

Medium
Confidence
93% confidence
Finding
`auto_invoke: true` allows the skill to activate automatically, but the manifest does not define narrow activation criteria, exclusions, or user-confirmation requirements. In a broad stock-analysis context, this can cause unintended execution on ordinary conversation, exposing secrets to the runtime and triggering network calls without sufficiently explicit user intent.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The example phrases are generic stock-analysis requests like 'Analyze AAPL' and 'Compare AAPL, MSFT, NVDA', which are highly likely to overlap with normal user prompts. Combined with auto-invocation, this increases the chance of accidental skill activation and unintentional external API usage in situations where the user did not explicitly request this particular skill.

Session Persistence

Medium
Category
Rogue Agent
Content
## OpenClaw scheduled tasks (morning/evening brief and radar)

To set up morning brief, evening brief, or daily radar in OpenClaw, use **only** the official OpenClaw cron format and create jobs via the CLI or Gateway cron tool. Do not edit `~/.openclaw/cron/jobs.json` directly.

- Reference: the `jobs` array in `config/openclaw-cron.example.json`; each item is one `cron.add` payload (fields: `name`, `schedule: { kind, expr, tz }`, `sessionTarget: "isolated"`, `payload: { kind: "agentTurn", message: "..." }`, `delivery`).
- Example (morning brief): `openclaw cron add --name "Stock morning brief" --cron "0 9 * * 1-5" --tz Asia/Shanghai --session isolated --message "Use stock-copilot-pro to generate morning brief: run brief --type morning --max-items 8 --format chat" --announce`. To deliver to Feishu, add `--channel feishu --to <group-or-chat-id>`.
Confidence
78% confidence
Finding
The skill documentation explicitly guides users to create persistent scheduled jobs that send agent messages on a recurring basis. Persistence itself is not inherently malicious, but recurring autonomous execution increases risk because it can repeatedly access watchlist data, use credentials, and perform network activity without fresh user intent each time.

Session Persistence

Medium
Category
Rogue Agent
Content
- Does not store API keys in logs, reports, or evolution state.
- Runtime persistence is limited to `.evolution/tool-evolution.json` (metadata + parameter templates only).
- Watchlist state is stored at `config/watchlist.json` (bootstrap from `config/watchlist.example.json`).
- OpenClaw scheduled tasks: see `config/openclaw-cron.example.json`. Create jobs with the official format (`schedule.kind`, `payload.kind`, `sessionTarget`, etc.) via `openclaw cron add` or the Gateway cron tool; do not paste or merge the example JSON into `~/.openclaw/cron/jobs.json` (schema mismatch can cause Gateway parse failure or crash). Set `delivery.channel` and `delivery.to` for your channel (e.g. feishu).
- External source URLs remain hidden by default; only shown when `--include-source-urls` is explicitly enabled.
- No package installation or arbitrary command execution is performed by this skill script.
- Research-only output. Not investment advice.
Confidence
80% confidence
Finding
This section confirms ongoing persistence via local state files and scheduled-task setup guidance, which enables the skill to retain behavioral state and support repeated autonomous runs. In a credentialed, auto-invokable skill, retained state and cron-based execution can amplify the effect of mistakes, prompt-routing errors, or future unsafe changes.

Session Persistence

Medium
Category
Rogue Agent
Content
{
  "_comment": "OpenClaw cron.add payload examples. Do not write into ~/.openclaw/cron/jobs.json. Create each job via CLI (openclaw cron add) or Gateway cron tool; set delivery.channel and delivery.to for your channel (e.g. feishu).",
  "jobs": [
    {
      "name": "Stock morning brief",
Confidence
60% 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.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The function returns user-facing natural-language strings in Chinese such as classification and signal labels, but there is no indication that the skill is intentionally China-region-specific or that users can opt into this locale. This creates a language/locale policy issue because the skill forces a specific language in generated outputs.

Natural-Language Policy Violations

Medium
Confidence
99% confidence
Finding
The thesis-building logic embeds many Chinese phrases for factors, evidence labels, risk descriptions, scenario conditions, and KPI names. Because the file does not offer a language choice or clearly justify a fixed locale, these strings constitute a natural-language policy violation under the language/locale rule.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The skill hardcodes multiple Chinese-language names such as index labels and display text regardless of user preference or locale. This can violate language/locale policy when a specific language is imposed without opt-in or a documented region-specific justification.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The fallback title '未命名热点' is hardcoded in Chinese and will be shown even when the user's preferred language is unknown. This is a natural-language locale constraint without any visible mechanism for user choice.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The source configuration injects Chinese-language query/input text directly, which can force Chinese-language behavior regardless of the user's preferred language or locale. The file does not provide any user choice, opt-in, or justification that this command is intentionally limited to a Chinese-language market workflow.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
When no topics are found, the command returns a fallback title and topic in Chinese, which imposes a language choice on all users. There is no indication that the user can opt into Chinese output or that the command is region-restricted.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/analyze.e2e.test.mjs:22

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/brief.e2e.test.mjs:16

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/compare.e2e.test.mjs:14

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/radar.e2e.test.mjs:14

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/regression.test.mjs:95

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/watch.e2e.test.mjs:16