Back to skill

Security audit

Equity Analyst

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its Korean stock-analysis purpose, but it ships under-documented report scripts with unsafe shell-based browser automation and local file writes that users should review before installing.

Review this skill before installing if you do not want an agent running local Python scripts, opening an OpenClaw browser profile, scraping Naver Finance, or writing report files into a local workspace. Treat the stock outputs as informational only, not financial advice. The safest path is to use only the documented analyzer/scraper workflow and avoid the daily or morning report scripts unless their shell invocation and hardcoded paths are fixed.

Vulnerability Patterns
  • 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
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/daily_popular_report.py:14
Finding
Shell Command Injection Through Unescaped Browser Command Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/daily_popular_report.py:14-19, 25-35` **Vulnerability Type**: OS command injection caused by unsafe shell invocation **Risk Level**: High ### Complete Code Snippet ```python def run_cmd(cmd, timeout=30): """Run shell command and return output.""" result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=timeout ) return result.stdout, result.stderr, result.returncode def browser_open(url): """Open URL in openclaw browser.""" cmd = f'openclaw browser --browser-profile openclaw open "{url}"' out, err, rc = run_cmd(cmd) return rc == 0 def browser_snapshot(save_path=None): """Get snapshot of current page. Optionally save to file.""" if save_path: cmd = f'openclaw browser --browser-profile openclaw snapshot > "{save_path}" 2>&1' out, err, rc = run_cmd(cmd, timeout=20) ``` ### Technical Analysis `run_cmd()` passes a dynamically constructed string to `subprocess.run()` with `shell=True`. Both `browser_open()` and `browser_snapshot()` interpolate function arguments directly into quoted shell commands without shell escaping. Quotation marks alone do not provide a security boundary. An argument containing a quote followed by shell syntax can terminate the quoted value and append another command. Redirection syntax is also deliberately interpreted by the shell in `browser_snapshot()`. The current `main()` function primarily supplies fixed URLs and stock-derived URLs containing six-digit tickers, which reduces exposure through the default execution path. However, the helper functions themselves accept unrestricted strings and are callable by imported code or future integrations. Any path that forwards untrusted input to these functions creates a command-execution vulnerability. ### Attack Path 1. An attacker influences a URL or snapshot output path passed to `browser_open()` or `bro ...[truncated 1289 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `shell=True` and pass each command as a separate argument: ```python def run_cmd(args, timeout=30): return subprocess.run( args, shell=False, capture_output=True, text=True, timeout=timeout, check=False ) def browser_open(url): result = run_cmd([ "openclaw", "browser", "--browser-profile", "openclaw", "open", url, ]) return result.returncode == 0 ``` 2. Do not use shell redirection to save snapshots. Capture standard output and write it through Python: ```python from pathlib import Path def browser_snapshot(save_path=None): result = run_cmd([ "openclaw", "browser", "--browser-profile", "openclaw", "snapshot", ], timeout=20) if result.returncode != 0: return None if save_path: destination = Path(save_path).resolve() destination.write_text( result.stdout + result.stderr, encoding="utf-8" ) return result.stdout ``` 3. Restrict output paths to an explicitly approved directory and reject paths that escape it after canonicalization. 4. Validate stock tickers with a full-match expression such as `r"\d{6}"` before constructing URLs. 5. Restrict browser destinations to HTTPS and an allowlist of expected Naver hostnames. 6. Add tests containing quotation marks, shell metacharacters, redirection operators, and path traversal sequences to verify that arguments cannot become executable shell syntax. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/scrape_naver.py:18
Finding
Unpinned Third-Party Dependency Installation Guidance<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scrape_naver.py:18-23` **Vulnerability Type**: Unpinned dependency and non-reproducible package installation **Risk Level**: Medium ### Complete Code Snippet ```python try: import requests from bs4 import BeautifulSoup except ImportError: print("Error: requests and beautifulsoup4 are required. Install with: pip install requests beautifulsoup4") sys.exit(1) ``` ### Technical Analysis The script instructs users to install `requests` and `beautifulsoup4` directly from the configured Python package index without specifying reviewed versions, hashes, a lock file, or an approved repository. The referenced package names are legitimate and there is no evidence that this project intentionally requests a malicious dependency. Nevertheless, resolving mutable latest versions at installation time makes the environment non-reproducible and leaves installation dependent on the integrity of the configured package index, resolver configuration, and whatever releases are current at that time. Without integrity hashes, package content cannot be verified against a project-approved artifact. A compromised package release, package index, mirror, or local package configuration could therefore introduce code that executes during installation or later import. ### Attack Path 1. A user runs `scripts/scrape_naver.py` without the required modules installed. 2. The script displays `pip install requests beautifulsoup4`. 3. The user follows the instruction in an environment using an untrusted or compromised index, mirror, or resolver configuration. 4. Pip retrieves an unreviewed package version without checking it against project-pinned hashes. 5. Malicious code may execute during package installation or when the package is imported by the script. 6. That code obtains the privileges and data access of the Python process. This finding is supply-chain exposure rather than evidence that the named upstream p ...[truncated 634 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a reviewed dependency manifest with exact versions, for example: ```text requests==<reviewed-version> beautifulsoup4==<reviewed-version> ``` 2. Generate and verify cryptographic hashes for all direct and transitive dependencies, then install using: ```bash python -m pip install --require-hashes -r requirements.txt ``` 3. Commit a lock file generated by a dependency-locking tool and review dependency updates before merging them. 4. Configure installation to use an approved HTTPS package repository and avoid untrusted extra indexes. 5. Install dependencies inside a dedicated virtual environment under a non-administrative account. 6. Replace the generic installation message with a reference to the locked project manifest: ```python print( "Missing dependencies. Install reviewed packages with: " "python -m pip install --require-hashes -r requirements.txt" ) ``` 7. Add automated dependency vulnerability and provenance checks to the release process. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (30)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the code only scrapes or exposes a CLI extractor while the skill claims to compute scores and recommendations, users may rely on outputs that are not actually produced by the documented logic. This kind of functional deception is dangerous because it defeats review, masks real capabilities, and can enable unauthorized collection or execution under the cover of a benign finance-analysis label.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
If the code only scrapes or exposes a CLI extractor while the skill claims to compute scores and recommendations, users may rely on outputs that are not actually produced by the documented logic. This kind of functional deception is dangerous because it defeats review, masks real capabilities, and can enable unauthorized collection or execution under the cover of a benign finance-analysis label.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the code only scrapes or exposes a CLI extractor while the skill claims to compute scores and recommendations, users may rely on outputs that are not actually produced by the documented logic. This kind of functional deception is dangerous because it defeats review, masks real capabilities, and can enable unauthorized collection or execution under the cover of a benign finance-analysis label.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
If the code only scrapes or exposes a CLI extractor while the skill claims to compute scores and recommendations, users may rely on outputs that are not actually produced by the documented logic. This kind of functional deception is dangerous because it defeats review, masks real capabilities, and can enable unauthorized collection or execution under the cover of a benign finance-analysis label.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the code only scrapes or exposes a CLI extractor while the skill claims to compute scores and recommendations, users may rely on outputs that are not actually produced by the documented logic. This kind of functional deception is dangerous because it defeats review, masks real capabilities, and can enable unauthorized collection or execution under the cover of a benign finance-analysis label.

Context-Inappropriate Capability

High
Confidence
93% confidence
Finding
The skill performs external browser automation and local process execution, which materially expands its capability beyond passive equity analysis. In context, this is more dangerous because the script consumes live web content and constructs shell commands, increasing exposure to injection, unintended navigation, and abuse of the host environment.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def run_cmd(cmd, timeout=30):
    """Run shell command and return output."""
    result = subprocess.run(
        cmd,
        shell=True,
        capture_output=True,
Confidence
99% confidence
Finding
Using subprocess.run with shell=True is a classic tool-parameter abuse pattern because shell metacharacters are interpreted rather than treated as data. Here the helper is reused by browser_open and browser_snapshot with interpolated values, so a crafted URL or path could trigger arbitrary command execution on the host.

Lp3

Medium
Category
MCP Least Privilege
Confidence
80% confidence
Finding
The skill advertises executable scripts and browser/network-based automation but does not declare any explicit tool scope or permission boundaries. In agent environments, this creates an over-broad trust surface where file, shell, and network actions may be available without clear least-privilege constraints, increasing the risk of unintended scraping, local file access, or command execution.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill provides investment recommendations and attractiveness scores without a clear warning that the output is informational only and not financial advice. In this context, users may over-trust deterministic-looking scores and recommendations, leading to real financial harm or compliance issues if the model produces incorrect or stale analysis.

Ae2

Medium
Category
analysis-evasion
Confidence
90% confidence
Finding
Artifact content does not match its filename extension

Natural-Language Policy Violations

Medium
Confidence
81% confidence
Finding
The news-scoring logic includes Korean-specific keywords and the example input uses Korean company data, but the script does not state that it is limited to Korean-language inputs or provide any language selection mechanism. This creates a locale-specific behavior that is implicit rather than user-opted or clearly justified.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest description states the skill provides an Investment Attractiveness Score and BUY/HOLD/AVOID recommendations. In the implemented verdict logic, scores from 65 to 79.9 produce "BUY_LEAN", which expands the documented recommendation set beyond what the skill claims to return.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script always prints the full structured analysis JSON to stderr, which can be captured by orchestrators, logs, CI systems, or parent processes without the user's awareness. Even if the current data seems non-sensitive, unsolicited secondary-channel output can leak proprietary analysis inputs, company-specific context, or future extensions that may include confidential data.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_cmd(cmd, timeout=30):
    """Run shell command and return output."""
    result = subprocess.run(
        cmd,
        shell=True,
        capture_output=True,
Confidence
98% confidence
Finding
This helper executes shell commands with shell=True, which turns any interpolated string into shell syntax. In this script it is used to build browser commands containing a URL and file path, so if either value becomes attacker-controlled or malformed, it can lead to command injection and arbitrary local command execution.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
Natural-language strings, parsing logic, and report output assume Korean content such as '인기 검색 종목', '더보기', and Korean report labels. This effectively forces a specific language/locale behavior without user opt-in or documented justification.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest describes an equity-analysis skill that analyzes Korean stocks and returns a score and BUY/HOLD/AVOID recommendation. This script not only performs analysis, but also persists a daily report to disk and includes delivery behavior aimed at sending the report to LINE, which is a broader reporting/notification workflow than the stated analysis function.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The top-level documentation explicitly states that the script sends a LINE report. No LINE API call, messaging client, or outbound message delivery exists in the code; the script only prints a preview and writes a text file.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes an equity analyst skill that analyzes Korean stock tickers and returns an Investment Attractiveness Score and BUY/HOLD/AVOID recommendation based on the prompt. This file hardcodes five stocks and produces a batch morning report, which is a materially different product and interaction model from the claimed single-stock analyst behavior.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
python_exe = "C:/Users/IM/AppData/Local/Programs/Python/Python310/python.exe"

    try:
        result = subprocess.run(
            [python_exe, script_path, '--input', input_path],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
python_exe = "C:/Users/IM/AppData/Local/Programs/Python/Python310/python.exe"

    try:
        result = subprocess.run(
            [python_exe, script_path, '--input', input_path],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The generated report strings and labels are fixed in Korean, and the skill does not offer any user opt-in or configuration for language selection. This creates a locale constraint in natural-language output without explicit choice or justification.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The report header and column labels are hardcoded in Korean, which imposes a specific language on all users of the script. There is no indication that the user can choose the output language or that the script is intentionally limited to a Korean-only context.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The manifest description is written as an instruction for a Korean-stock analyst persona entirely in Korean, which can imply a forced language/locale behavior. While the skill is scoped to Korean equities, the file does not explicitly state that responses may be in Korean only or offer the user a language choice, so the locale constraint is not clearly documented as a justified policy exception.

Intent-Code Divergence

Low
Confidence
81% confidence
Finding
The module documentation describes the script as taking input data and outputting a structured analysis report. However, when executed as __main__, the script additionally prints a JSON block to stderr for debugging/capturing, which is an undocumented extra output behavior and can contradict consumer expectations about outputs.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The module docstring states that the script 'Generates report and sends to LINE'. In the actual implementation, the report is written to a local file and the LINE send step is only mentioned in comments as something a cron/system event or manual command could do later, so the documented behavior overstates what the code actually performs.

Static analysis

No suspicious patterns detected.