Back to skill

Security audit

Buffett Analysis

Security checks for vulnerabilities and agentic risk

Overview

This skill is an investment-analysis helper, but it also requires local command execution and automatic GitHub publication in ways that need careful review before use.

Treat this as a Review item. Before installing, require changes that remove shell=True command construction, use safe temporary files, and make repository writes and GitHub pushes opt-in with a visible diff and explicit approval. Do not run it in an environment with valuable GitHub credentials or broad filesystem access unless those issues are fixed or tightly sandboxed.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fetch_company_data.py:23
Finding

Arbitrary Command Execution Through Unsanitized Company Input

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_company_data.py, lines 23-28 and 37-42
Vulnerability Type: OS command injection
Risk Level: High

Vulnerable Code

python
def mcporter(tool, **kwargs):
    args = " ".join(f"{k}={v}" for k, v in kwargs.items())
    cmd = f"mcporter call {tool} {args}"
    try:
        r = subprocess.run(
            cmd,
            shell=True,
            capture_output=True,
            text=True,
            env=ENV,
            timeout=60
        )
python
def mcporter_to_file(tool, out_path, **kwargs):
    args = " ".join(f"{k}={v}" for k, v in kwargs.items())
    cmd = f"mcporter call {tool} {args}"
    try:
        with open(out_path, "w") as f:
            subprocess.run(
                cmd,
                shell=True,
                stdout=f,
                stderr=subprocess.DEVNULL,
                env=ENV,
                timeout=60
            )

Technical Analysis

The functions concatenate argument names and values into a single command string and then execute it using shell=True. The keyword value originates from sys.argv[1] and is repeatedly passed to these functions without validation or shell escaping.

Consequently, shell metacharacters in a crafted company keyword are interpreted by the operating-system shell instead of being treated as literal company-name characters. The affected helpers are used for company information, financial statements, valuation, market data, shareholder information, and industry queries.

Merely applying string quoting would remain fragile. The correct security boundary is to avoid invoking a shell and pass every argument as a distinct element in an argument vector.

Attack Path

  1. An attacker supplies a crafted company name or ticker containing shell syntax.
  2. main() reads the value from sys.argv[1].
  3. fetch_company_data() passes it as the ` ...[truncated 1114 chars]
Remediation
View remediation

Remediation Suggestions

  • Eliminate shell=True and construct an argument list:

    python
    def mcporter(tool, **kwargs):
        cmd = ["mcporter", "call", tool]
        cmd.extend(f"{key}={value}" for key, value in kwargs.items())
    
        try:
            result = subprocess.run(
                cmd,
                shell=False,
                capture_output=True,
                text=True,
                env=ENV,
                timeout=60,
                check=True,
            )
            if result.stdout.strip():
                return json.loads(result.stdout)
        except (
            subprocess.CalledProcessError,
            subprocess.TimeoutExpired,
            json.JSONDecodeError,
        ):
            return None
    
  • Apply an allowlist to MCP tool names rather than accepting arbitrary tool identifiers.

  • Validate company codes and tickers against market-specific formats.

  • For company names that require broader character support, enforce a reasonable length limit and reject control characters.

  • Check subprocess return codes and handle failures explicitly.

  • Run the data collector under a restricted operating-system account without unnecessary repository, credential, or sensitive-file access.

  • Add regression tests containing shell metacharacters and verify that they are passed as literal argument content.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_company_data.py:37
Finding

Predictable Shared Temporary Files Permit File Clobbering and Report Data Poisoning

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_company_data.py, lines 37-46 and 98; scripts/fetch_us_company_data.py, lines 34-41
Vulnerability Type: Unsafe temporary-file handling
Risk Level: Medium

Vulnerable Code

python
def mcporter_to_file(tool, out_path, **kwargs):
    args = " ".join(f"{k}={v}" for k, v in kwargs.items())
    cmd = f"mcporter call {tool} {args}"
    try:
        with open(out_path, "w") as f:
            subprocess.run(
                cmd,
                shell=True,
                stdout=f,
                stderr=subprocess.DEVNULL,
                env=ENV,
                timeout=60
            )
        if os.path.getsize(out_path) > 2:
            with open(out_path) as f:
                return json.load(f)
python
metrics = mcporter_to_file(
    "fintool-company.get_financial_analytics_metric",
    "/tmp/fam_raw.json",
    keyword=keyword
)
python
safe_sym = symbol.upper().replace('^', 'IDX_').replace('/', '_')
out_path = f"/tmp/us_market_{query_type}_{safe_sym}.json"
if os.path.exists(out_path):
    with open(out_path, encoding='utf-8') as f:
        data = json.load(f)
        if 'error' not in data:
            return data

Technical Analysis

The A-share data collector writes financial data to the fixed path /tmp/fam_raw.json. On systems where /tmp is shared by multiple users or processes, another local process can create that path in advance, including as a symbolic link. Opening the path with normal write mode follows symbolic links and may overwrite the link target with the privileges of the Skill process.

The US-market wrapper derives its input file from a predictable query type and ticker. It checks only whether the path exists and whether the parsed object lacks an error field. It does not:

  • Remove a pre-existing file before launching the subprocess.
  • Verify that the subpro ...[truncated 2032 chars]
Remediation
View remediation

Remediation Suggestions

  • Use tempfile.NamedTemporaryFile, tempfile.TemporaryDirectory, or tempfile.mkstemp to create unpredictable files atomically with restrictive permissions.
  • Avoid shared intermediate files by parsing captured subprocess output directly.
  • If a delegated tool requires a file, pass a freshly generated output path to it rather than assuming a predictable path.
  • Reject symbolic links and verify file ownership and type before reading or replacing files.
  • Require subprocess.run(..., check=True) to complete successfully before consuming output.
  • Remove stale files before subprocess execution and confirm that the resulting file was created or updated by the current invocation.
  • Validate returned JSON against an explicit schema, including expected ticker, query type, and response fields.
  • Write final reports atomically by creating a restricted temporary file and using os.replace() after successful serialization.
  • Add concurrency tests and hostile /tmp tests covering symlinks, stale files, malformed JSON, and pre-planted valid JSON.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:265
Finding

Mandatory GitHub Publication Exceeds the Minimum Privileges Required for Analysis

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 265-267, 323, 388, and 445
Vulnerability Type: Excessive repository access and unconfirmed external publication
Risk Level: Medium

Vulnerable Instruction Excerpt

The operative instructions require the following behavior for every completed company analysis:

text
Every time a fundamental analysis is completed, the report data must be
written to the frontend display.

Read alpha-factor-lab/fundamental-reports.json.

After writing, commit and push to GitHub to synchronize the online page.

The industry-analysis workflow imposes the same requirement:

text
Write the industry analysis to alpha-factor-lab/fundamental-reports.json,
then commit and push.

Technical Analysis

The declared core functionality is public-company and industry analysis. Producing that analysis requires market-data access and local report generation, but it does not inherently require:

  • Write access to another repository.
  • Access to ambient Git credentials.
  • Permission to create commits.
  • Permission to publish generated content externally.

The instructions make repository mutation and GitHub publication mandatory rather than optional. They do not require the user to confirm the target repository, inspect the generated diff, approve the report content, or authorize public disclosure immediately before the push.

This violates least-privilege principles because a read-and-analyze workflow is expanded into a persistent external write operation. The risk is heightened because reports may contain user-supplied company names, generated conclusions, inaccurate data, or poisoned data from other findings.

Attack Path

  1. A user requests a company or industry analysis.
  2. The Skill generates a report using public data and user-provided input.
  3. The instructions require the Agent to locate and modify alpha-factor-lab/fundamental-reports.json.

...[truncated 1165 chars]

Remediation
View remediation

Remediation Suggestions

  • Make repository export optional and default to local-only report generation.
  • Require explicit user confirmation immediately before any commit or push.
  • Display the repository, branch, destination file, visibility, and proposed diff before requesting approval.
  • Separate report generation from publication into distinct commands or workflows.
  • Use a narrowly scoped credential that can write only to the intended repository and branch.
  • Prefer pull requests over direct pushes so generated content receives review.
  • Validate and sanitize all generated JSON before staging it.
  • Prevent commits when report data is incomplete, schema-invalid, stale, or derived from failed data-collection calls.
  • Record the data sources and generation timestamp in the proposed report.
  • Allow administrators to disable external publication entirely.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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 (22)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

该描述把技能定位为一个可直接完成上市公司/行业深度研究的分析器,但给出的代码块只是底层数据抓取组件。其核心行为是调用多个金融数据接口收集结构化财务和市场数据,然后落盘到临时 JSON 文件,属于“数据准备/采集”而非“分析报告生成”。此外,描述中明确提到的新闻、高管、管理层、PR、行业一页纸等能力,在代码中均未体现。虽然抓取财报、估值、行业分类、同行等内容与声明部分一致,但整体上代码能力显著少于声明,且主要用途不同,因此属于描述与实际行为不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

该代码块的核心功能是“美股公司基础数据抓取器”,而不是完整的“巴菲特视角上市公司/行业深度分析”技能。它通过 subprocess 调用另一个 us-market 脚本,获取 profile、quote、financials、analyst、dividends 等有限数据,并写入临时 JSON 文件。与声明相比,代码覆盖面明显更窄:仅限美股、仅限公司级数据采集、且没有真正的分析逻辑或报告生成能力,也未体现新闻采集、高管研究、管理层分析、行业分析、A股/港股支持等关键承诺。因此描述与实际行为存在实质性不匹配。

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill is presented as an analysis/reporting tool, but it instructs the agent to modify a repository data file and commit/push changes to GitHub as a mandatory step. This is dangerous because a simple research request can trigger persistent state changes and external publication, enabling unintended content publication, repository tampering, or abuse of configured credentials.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill directs the agent to update a local JSON file and push the result to GitHub without any user-facing warning that persistent project files will be modified and content may be published externally. This violates user expectation and creates a strong risk of unauthorized or accidental changes, especially if Git credentials are available in the environment.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The industry-analysis path repeats the same hidden side effect: analysis output is persisted into project files and then published to GitHub, despite the skill being described as a research capability. This increases risk because even broad, ambiguous trigger phrases can lead to unintended repository modifications and public dissemination.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The same unsafe pattern appears in the industry workflow: persistent updates and GitHub publication are performed without explicit warning or consent. In context, this is especially risky because the skill’s broad matching logic can funnel innocuous analytical questions into a workflow with external side effects.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/fetch_company_data.py (reported line 20)May include surrounding context.

python
import time
from datetime import datetime

ENV = os.environ.copy()
ENV["PATH"] = "/home/node/.local/bin:" + ENV.get("PATH", "")

def mcporter(tool, **kwargs):

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

User input is repurposed as part of a tool invocation string without validation or safe argument separation. In this skill context, users are expected to provide arbitrary company names/codes, so the attack surface is directly exposed and an attacker can turn a data-fetch request into arbitrary shell execution.

Content

Scanner excerpt · scripts/fetch_company_data.py (reported line 28)May include surrounding context.

python
args = " ".join(f"{k}={v}" for k, v in kwargs.items())
    cmd = f"mcporter call {tool} {args}"
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, env=ENV, timeout=60)
        if r.stdout.strip():
            return json.loads(r.stdout)
    except (json.JSONDecodeError, subprocess.TimeoutExpired) as e:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

The file-output code path repeats the same unsafe pattern and is equally exploitable through crafted parameter values. Because the skill is designed for broad analyst-style prompting, hostile inputs can plausibly reach this helper during ordinary operation, making exploitation more dangerous than in a tightly internal-only script.

Content

Scanner excerpt · scripts/fetch_company_data.py (reported line 42)May include surrounding context.

python
cmd = f"mcporter call {tool} {args}"
    try:
        with open(out_path, "w") as f:
            subprocess.run(cmd, shell=True, stdout=f, stderr=subprocess.DEVNULL, env=ENV, timeout=60)
        if os.path.getsize(out_path) > 2:
            with open(out_path) as f:
                return json.load(f)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill invokes shell commands, writes files, and likely relies on environment-backed credentials, but the manifest does not declare any tool scope or permission boundaries. This creates an under-specified privilege surface where an agent may execute higher-risk actions than a user would reasonably expect from a research skill.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger phrases are broad enough to match ordinary investment-related conversation, which raises the chance the skill activates in contexts where the user did not intend a full workflow. In this skill, overbroad activation is more dangerous because activation can lead not just to analysis but also to file writes and repository publication.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The mode definitions are ambiguous and can route a user into materially different workflows without clear consent boundaries. Because some branches include shell execution, persistent writes, and publishing, weak routing logic increases the risk of unintended privileged actions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The instructions prescribe a fixed "巴菲特语言风格" and output conventions, including how the report should be written, without indicating that the user may choose another language or style. This imposes a language/locale-related presentation constraint without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Lines L129-L136 prescribe Chinese number units ("亿", "万") and named platform-specific output formats, while the document overall is entirely in Chinese and does not indicate that this locale is optional. Under the policy, forcing a specific language/locale without user opt-in is a natural-language policy violation unless clearly documented as region-specific, which is not stated here.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code invokes external commands via subprocess with shell=True and writes fetched data to files under /tmp, but the user-facing docstring and CLI usage text only describe data retrieval. While there are progress prints, they do not disclose that the script executes shell commands or creates output files as side effects.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

The script builds a shell command by concatenating the tool name and unquoted parameter values, then executes it with shell=True. Because user-controlled input such as keyword flows into kwargs, an attacker can inject shell metacharacters and execute arbitrary commands on the host running the skill.

Content

Scanner excerpt · scripts/fetch_company_data.py (reported line 28)May include surrounding context.

python
args = " ".join(f"{k}={v}" for k, v in kwargs.items())
    cmd = f"mcporter call {tool} {args}"
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, env=ENV, timeout=60)
        if r.stdout.strip():
            return json.loads(r.stdout)
    except (json.JSONDecodeError, subprocess.TimeoutExpired) as e:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

This second subprocess path has the same command-construction flaw and additionally writes command output to a file, which can conceal exploitation while still executing attacker-supplied shell fragments. In a skill that accepts company names/tickers from users, this makes remote command injection realistically reachable through normal usage.

Content

Scanner excerpt · scripts/fetch_company_data.py (reported line 42)May include surrounding context.

python
cmd = f"mcporter call {tool} {args}"
    try:
        with open(out_path, "w") as f:
            subprocess.run(cmd, shell=True, stdout=f, stderr=subprocess.DEVNULL, env=ENV, timeout=60)
        if os.path.getsize(out_path) > 2:
            with open(out_path) as f:
                return json.load(f)

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest promises support for A股、港股、美股 and automatic collection of财报、估值、行业数据、公司新闻、高管信息, but this script only fetches company base info, industry, plates, financial statements, valuation, quote snapshots, shareholders, and peers through APIs that appear centered on domestic stock/plate taxonomy such as 申万行业 and plate stocks. There is no implementation here for company news, executive information, management analysis inputs, PR/news collection, or explicit handling of Hong Kong/U.S. ticker-specific workflows described in the skill description.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The module docstring and all user-facing usage text are written only in Chinese, and later runtime messages also continue in Chinese. This imposes a specific language on users without opt-in or any indication that the skill is intentionally limited to a Chinese-speaking context, which matches the locale-policy violation criteria.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/fetch_us_company_data.py (reported line 30)May include surrounding context.

python
cmd.extend([f'--{k}', str(v)])
    
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
    except subprocess.TimeoutExpired:
        return None

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file title and all instructions are written in Chinese, which effectively fixes the skill content to a specific language. Under the policy rule, locale or language constraints should either be optional for the user or explicitly justified as region-specific; this template does neither.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

Natural-language instructions, description, and usage text are presented only in Chinese, which imposes a specific language on users without any documented opt-in or alternative. This matches the language/locale policy concern for skills that force a language choice.

Content

No source excerpt is available for this finding.