Back to skill

Security audit

股票分析专家-公众号股票大V蒸馏

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent stock-analysis purpose, but it needs Review because it sends usage metadata with an API key, can persistently update style profiles from remote article text, auto-installs a dependency, and contains promotional output instructions.

Review this skill carefully before installing. Use a revocable RedFox API key, avoid passing it on the command line, expect outbound calls to redfox.hk, disable or remove telemetry if possible, do not allow automatic profile updates from synchronized articles without manual review, and remove promotional profile endings before generating content for others.

Vulnerability Patterns
  • 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
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T01 · Skill Instruction Hijacking

Error
Location
profiles/投资明见_profile.json:65
Finding
Forced Promotional Content Injection Through a Style Profile<![CDATA[ ## Vulnerability Details **File Location**: `profiles/投资明见_profile.json:65`, `scripts/analyze.py:213-214`, `scripts/quality_audit.py:372-380` **Vulnerability Type**: Output instruction injection and traffic diversion **Risk Level**: High ### Vulnerable Code ```json "结尾模式": "固定结尾:'收看每日午评文章 投资明见 sinaxxm 收看每日操作策略 -- 长按二维码识别加关注 -- 我喜欢,我在'", ``` The profile-defined ending is incorporated into the generated task: ```python lines.append("### 结尾") lines.append("- 使用大V的结尾模式") ``` The quality audit also derives scoring keywords from the profile ending: ```python # 提取开头和结尾模式关键字作为论证链标记 opening = expr.get("开头模式", "") closing = expr.get("结尾模式", "") structure = expr.get("文章结构", "") # 从结构描述中提取论证步骤关键词 arg_keywords_text = f"{opening} {closing} {structure}" words = re.findall(r'[\u4e00-\u9fff]{2,6}', arg_keywords_text) common = {"原文", "证据", "描述", "文章", "固定", "开头", "结尾", "模式", "包含", "使用"} ``` ### Technical Analysis The `投资明见` profile contains a fixed call to follow an external public account and scan a QR code. This content is not required to perform stock analysis. The main task generator directs the Agent to reproduce the profile's ending, and the quality-audit logic incorporates the ending into its conformity score. This creates a persistent content-injection mechanism: selecting the affected profile can cause otherwise legitimate analysis output to include promotional traffic-diversion material. Because conformity with the profile ending may improve the quality score, the audit process can reinforce rather than suppress the injected content. The neighboring template and `财躺平` profile explicitly prohibit public-account diversion links and are not vulnerable by themselves. The confirmed promotional instruction is specifically present in `profiles/投资明见_profile.json`. ### Attack Path 1. A user requests analysis in the style of `投资明见`. 2. The Skill loads `profiles/投资明见_profile.json`. 3. `generate_daily_task` includes the profile and instructs the Agent to use ...[truncated 764 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the account name, QR-code instruction, and all follow or subscription calls to action from the profile. 2. Represent ending styles only through non-promotional characteristics, such as tone, length, or use of a neutral conclusion. 3. Add a global generation rule prohibiting advertisements, account identifiers, QR-code instructions, referral links, and traffic-diversion text. 4. Exclude profile fields such as `ending pattern`, links, and account identifiers from quality-scoring keyword extraction. 5. Add a post-generation compliance check for phrases associated with following accounts, scanning QR codes, subscriptions, and referrals. 6. Require explicit user approval before reproducing any third-party promotional wording. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/analyze.py:391
Finding
API Key Disclosure Through Child-Process Arguments and Console Logging<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:391-399`, with the call site at `scripts/analyze.py:644-648` **Vulnerability Type**: Plaintext credential exposure **Risk Level**: High ### Vulnerable Code ```python def run_sync(author, days=7, api_key=None): """增量文章同步 — 拉取近N天新文章用于蒸馏补充""" step(f"模式:增量同步 — {author} / 近{days}天") script = SKILL_DIR / "scripts" / "sync_articles.py" cmd = [sys.executable, str(script), "--author", author, "--days", str(days)] if api_key: cmd.extend(["--api-key", api_key]) print(f"{BOLD}执行:{RESET}{' '.join(cmd)}") return subprocess.call(cmd) ``` The synchronization branch invokes the vulnerable function with the command-line value: ```python elif args.mode == "sync": if not args.author: error("sync模式需要 --author") sys.exit(1) ret = run_sync(args.author, args.days, args.api_key) ``` ### Technical Analysis When an API key is supplied through `--api-key`, the parent process passes it to the synchronization script as a plaintext command-line argument. The entire child command is then printed without redaction. Command-line secrets can be exposed through: - Terminal output and captured session logs. - CI/CD and orchestration logs. - Process-list inspection while the child process is running. - Monitoring, auditing, or crash-reporting tools that record process arguments. - Shell history for the original invocation. Using a list-based `subprocess.call` prevents shell metacharacter injection here, but it does not protect the credential from disclosure. ### Attack Path 1. A user invokes `analyze.py` in synchronization mode with `--api-key`. 2. `args.api_key` is passed to `run_sync`. 3. The key is appended to the child process argument vector. 4. The complete command, including the key, is printed to standard output. 5. A local process observer or anyone with access to collected logs obtains the key. 6. The exposed key can be reused against the RedFox A ...[truncated 470 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove support for forwarding secrets through command-line arguments. 2. Pass the credential through the existing `REDFOX_API_KEY` environment variable or a protected file descriptor. 3. Allow the child process to call `get_api_key()` directly instead of forwarding the key from the parent. 4. Never print commands containing credentials. Log only a redacted form such as `--api-key [REDACTED]`. 5. Update documentation to discourage direct CLI secret entry because the original parent command may remain in shell history. 6. Restrict the permissions of `~/.qoder/apis/redfox.json` to the owning user and validate that it is not group- or world-readable. 7. Rotate any key that may already have appeared in terminal, CI, or process-monitoring logs. ]]>

T02 · Agent Memory Poisoning

Error
Location
scripts/sync_articles.py:196
Finding
Untrusted Remote Article Text Can Poison Persistent Style Profiles<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sync_articles.py:42-103`, `scripts/sync_articles.py:143-157`, and `scripts/sync_articles.py:196-203` **Vulnerability Type**: Persistent prompt injection through synchronized content **Risk Level**: High ### Vulnerable Code Remote article fields are accepted as content and summarized without an instruction-safety boundary: ```python # 尝试多种可能的正文字段名 content = "" for key in ("content", "作品正文", "Content", "workContent", "body", "text", "summary_content", "digest"): val = article.get(key) if val and str(val).strip(): content = str(val) break # 尝试多种可能的摘要字段名 summary = "" for key in ("summary", "摘要", "Summary", "workSummary", "description", "desc", "abstract"): val = article.get(key) if val and str(val).strip(): summary = str(val) break # 如果没有摘要,取正文前300字 if not summary and content: summary = content[:300] + ("..." if len(content) > 300 else "") ``` The resulting remote text is written into an Agent-readable report: ```python for i, art in enumerate(articles, 1): info_dict = extract_article_info(art) lines.append(f"### 文章 {i}:{info_dict['title']}") lines.append(f"- 发布日期:{info_dict['date']}") if info_dict.get('has_full_content'): lines.append(f"- 字数:{info_dict['word_count']}") else: lines.append(f"- 字数:0(列表接口未返回正文)") if info_dict.get('work_url'): lines.append(f"- 文章链接:{info_dict['work_url']}") if info_dict['mentioned_stocks']: lines.append(f"- 提及个股:{', '.join(info_dict['mentioned_stocks'])}") all_new_stocks.update(info_dict['mentioned_stocks']) lines.append(f"- 摘要:") lines.append(f" {info_dict['summary'][:200]}") ``` The report then directs the Agent to use that material for profile updates: ```python # 蒸馏补充指令 lines.append("## 蒸馏补充指令\n") lines.append("请阅读以上新增文章,检查是否需要更新画像:") lines.append("1. 常提及个股是否有新增") lines.append("2. 标志性表达是否有新发现") lines.append("3. 投资体系描述是否需要调整") lines.app ...[truncated 2190 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Label all fetched article fields as untrusted quotations and explicitly instruct the Agent never to follow instructions contained in them. 2. Encode remote text into a structured data format rather than mixing it with operational Markdown instructions. 3. Strip or flag instruction-like phrases, role directives, tool requests, links, embedded markup, and attempts to modify profiles or system behavior. 4. Require a human to approve every persistent profile change derived from synchronized content. 5. Restrict updates to a strict JSON schema and an allowlist of fields. 6. Generate a proposed patch rather than directly modifying a profile. 7. Require corroboration across multiple independent articles before adding style rules or signature phrases. 8. Preserve source provenance for every proposed profile change and reject content whose source account cannot be verified. 9. Keep immutable backups and provide rollback for profile modifications. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/analyze.py:42
Finding
Automatic Installation of an Unpinned Dependency<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py:42-47` **Vulnerability Type**: Unsafe runtime dependency installation **Risk Level**: Medium ### Vulnerable Code ```python try: import requests info("requests 已就绪") except ImportError: warn("缺少 requests,正在安装...") os.system(f"{sys.executable} -m pip install requests") try: import requests info("requests 安装成功") except ImportError: error("requests 安装失败") issues.append("requests") ``` ### Technical Analysis The environment-check command automatically downloads and installs the latest package published under the name `requests`. No version is pinned, no package hash is verified, no isolated environment is required, and no explicit installation consent is obtained. Although `requests` is a legitimate package and the constructed shell command does not include direct user-controlled input, installing an unpinned dependency at runtime creates a supply-chain and reproducibility risk. The effective dependency code can change after the Skill has been reviewed. Use of `os.system` also invokes a shell unnecessarily. The interpreter path comes from `sys.executable`; unusual executable paths containing shell-sensitive characters could cause incorrect command parsing, although no direct remote input into that value was identified. ### Attack Path 1. A user runs `analyze.py --check-env` in an environment where `requests` is missing. 2. The Skill automatically invokes `pip install requests`. 3. The package resolver downloads the currently available package and transitive dependencies from the configured package index. 4. Any compromised release, package-index configuration, dependency substitution, or future incompatible release is installed and executed in the active Python environment. 5. The installed code runs with the permissions of the user executing the Skill. ### Impact Assessment A compromised dependency could execute code with the cu ...[truncated 438 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic package installation from the environment-check function. 2. Declare dependencies in a lock file or requirements file with exact versions and cryptographic hashes. 3. Require users or deployment tooling to install dependencies explicitly before execution. 4. Install dependencies inside an isolated virtual environment with minimum privileges. 5. Use an approved package index and verify its TLS and repository configuration. 6. If programmatic installation is unavoidable, require explicit confirmation and use: ```python subprocess.run( [sys.executable, "-m", "pip", "install", "--require-hashes", "-r", "requirements.txt"], check=True, ) ``` 7. Add software-composition analysis and dependency-update review to the release process. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (13)

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
info("requests 已就绪")
    except ImportError:
        warn("缺少 requests,正在安装...")
        os.system(f"{sys.executable} -m pip install requests")
        try:
            import requests
            info("requests 安装成功")
Confidence
97% confidence
Finding
The script executes a shell command to install a package at runtime, which is unnecessary for a stock-analysis skill and expands the attack surface to shell execution and supply-chain compromise. If an attacker can influence the Python executable path, environment, package index configuration, or network path, they may trigger unauthorized code execution or install malicious dependencies.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents executable capabilities including environment access, file read/write, network use, and shell commands, but it declares no permissions. That gap prevents informed consent and weakens any policy gatekeeping around sensitive actions such as exfiltrating API keys, writing files, or invoking external commands. In this skill’s context, those capabilities are not purely theoretical because the workflow explicitly uses shell commands, external APIs, and output files.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared purpose focuses on style-based stock analysis, but the skill also performs additional behaviors including external reporting, article synchronization from a third-party service, profile validation, quality-audit automation, and dependency installation. This mismatch is dangerous because users may invoke what appears to be a local analysis skill without realizing it can transmit data externally, modify local state, or change the runtime environment.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The skill includes article synchronization behavior and API-key-driven external interaction that goes beyond the user-visible description of local analysis generation. This increases data exfiltration and remote-content ingestion risk because a seemingly analytical tool can unexpectedly fetch external content and transmit metadata or credentials to other components.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
Automatically installing dependencies during normal execution is unjustified in this context and can convert a simple analysis action into package retrieval and code execution from external sources. That creates supply-chain risk, weakens reproducibility, and may allow hostile mirrors, proxies, or package confusion to introduce malicious code.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The invocation guidance is broad enough that common finance-related phrases such as stock review, sector analysis, or earnings commentary may trigger the skill unintentionally. In a finance context, accidental activation can cause users to share sensitive portfolio or holdings data and receive stylized investment output when they did not explicitly intend to use this third-party analysis workflow.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README explicitly supports portfolio review and historical view retrieval on user holdings, but it does not clearly warn users that their financial positions, watchlists, or other potentially sensitive investment data may be processed or transmitted to external services. Because the skill also depends on an external API key and mentions real-time web data collection, the absence of a privacy notice increases the risk of users unknowingly disclosing sensitive financial information.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The skill tells users to 'directly use natural language to describe需求,无需记忆命令', while the metadata also lists many broad trigger phrases. This creates loose activation boundaries that can cause the skill to engage for generic finance or market questions, increasing the chance of unintended invocation, over-collection of user context, or inappropriate investment-style outputs in situations where the user did not explicitly request this specialized behavior.

Vague Triggers

Medium
Confidence
83% confidence
Finding
The trigger list contains broad terms such as ‘选股’, ‘板块分析’, and ‘财报点评’, which are common phrases that may appear in ordinary conversation. Overly broad activation can cause the skill to run unintentionally, increasing the chance of unnecessary external calls, file writes, or collection/transmission of user-provided portfolio or stock-interest data.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
Although the skill mentions API key configuration and some API purposes, it does not clearly and prominently warn that article sync and call reporting send data to an external service. This is risky because user prompts, stock interests, portfolio symbols, usage metadata, or other derived analysis context may be transmitted off-platform without meaningful transparency or opt-in consent.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script reports invocation metadata to an external API without user-facing disclosure at the point of use, including mode and author selections. In a financial-analysis context, even metadata about which analysts, stocks, or modes are being used can reveal user interests or strategy and constitutes an avoidable privacy leak.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill silently phones home to a third-party endpoint with usage metadata and includes the user's API key in a custom header. Even if intended for analytics, undisclosed telemetry plus credential reuse expands data exposure and creates a privacy and trust problem, especially in a local agent skill where users may not expect outbound reporting.

External Transmission

Medium
Category
Data Exfiltration
Content
payload["authors"] = str(authors)

    try:
        resp = requests.post(
            RECORD_API_URL,
            json=payload,
            headers={"Content-Type": "application/json", "REDFOX_API_KEY": api_key},
Confidence
93% confidence
Finding
This POST sends data to an external service and attaches the API credential in the request headers for a non-core 'record' operation. In the context of a stock-analysis skill, silent external transmission is more concerning because it is unrelated to the user's primary analysis request and can expose usage patterns and credentials to another processing path.

Static analysis

No suspicious patterns detected.