Back to skill

Security audit

VN Stock Scanner

Security checks for vulnerabilities and agentic risk

Overview

This Vietnam stock skill is purpose-aligned, but it uses unsafe command execution patterns and disables TLS checks for financial data sources.

Review before installing. The skill does not appear malicious, but it should be fixed to run the scanner with structured arguments, validate ticker and keyword inputs, use package-relative paths, and remove verify=False before relying on its financial data. Treat its returned news and metrics as untrusted until TLS verification is restored.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:12
Finding
Shell Command Injection Through User-Controlled Command Arguments<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 12-25 **Vulnerability Type**: OS command injection caused by unsafe shell command construction **Risk Level**: High ### Vulnerable Code ```markdown - Trích xuất mã cổ phiếu (`ticker`): ví dụ `FPT`, `VCB`, `HPG`. - Sử dụng tool `exec` gọi lệnh: ```bash python3 /home/hoang/.openclaw/workspace/vn-stock-scanner/scripts/scanner.py ticker --ticker <mã_cổ_phiếu> ``` - Dùng thông tin trả về (P/E, P/B, EPS, Tỷ suất cổ tức...) để trả lời user và đưa ra nhận định ngắn gọn. ## 2. Quét tin tức và tin đồn (News & Rumor Scanner) Khi user hỏi "Có tin tức chứng khoán gì hot không?", "Tìm tin đồn", "Chủ tịch đăng ký mua bán": - Nhận diện từ khóa user quan tâm (`keywords`). Nếu user muốn tin chung chung thì bỏ trống. Nếu user muốn tin về mua/bán nội bộ, thì truyền `keywords="mua,bán,chủ tịch,đăng ký"`. - Sử dụng tool `exec` gọi lệnh: ```bash python3 /home/hoang/.openclaw/workspace/vn-stock-scanner/scripts/scanner.py news --keywords "<từ_khóa>" ``` ``` ### Technical Analysis The Skill directs the Agent to extract ticker symbols or keywords from user input and interpolate them into shell command templates executed through `exec`. The ticker placeholder is entirely unquoted. Consequently, shell operators, command separators, substitutions, redirections, and additional arguments may be interpreted by the shell rather than passed literally to `scanner.py`. The keyword placeholder is enclosed in double quotes, but this does not provide a security boundary. An attacker can include a double quote to terminate the quoted argument and then append shell syntax. The instructions do not require allowlist validation, escaping, or invocation through a non-shell argument array. Although `argparse` safely processes arguments after Python starts, it cannot protect against commands interpreted by the shell before `scanner.py` is launched. ### Attack Path 1. An attacker submits a stock-analysis or news reque ...[truncated 1482 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not construct shell command strings from user-derived values. 2. Invoke Python with a discrete argument array and without a shell, equivalent to: ```python subprocess.run( [ "python3", "/home/hoang/.openclaw/workspace/vn-stock-scanner/scripts/scanner.py", "ticker", "--ticker", ticker, ], shell=False, check=True, ) ``` 3. Validate tickers before invocation using a strict allowlist, such as uppercase ASCII letters and digits with a conservative maximum length: ```python if not re.fullmatch(r"[A-Z0-9]{1,10}", ticker): raise ValueError("Invalid ticker") ``` 4. Pass news keywords as a discrete argument rather than interpolating them into a quoted command. 5. Add defense-in-depth validation inside `scanner.py`, because callers other than the Skill may invoke it. 6. Update `SKILL.md` to explicitly prohibit shell interpolation and require structured process execution. 7. Run the scanner with a minimally privileged account and restrict access to credentials and sensitive files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/scanner.py:6
Finding
TLS Certificate Verification Disabled for External Data Sources<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scanner.py`, lines 6-40 **Vulnerability Type**: Improper certificate validation **Risk Level**: Medium ### Vulnerable Code ```python urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) ``` ```python def get_news(keywords=""): """Lấy tin tức mới nhất từ RSS của CafeF.""" url = "https://cafef.vn/tin-tuc-su-kien.rss" try: headers = {"User-Agent": "Mozilla/5.0"} resp = requests.get(url, headers=headers, timeout=10, verify=False) root = ET.fromstring(resp.content) ``` ```python def get_ticker_info(ticker): """Lấy thông tin cơ bản của mã chứng khoán VN qua API public của TCBS.""" url = f"https://apipubaws.tcbs.com.vn/tcanalysis/v1/ticker/{ticker.upper()}/overview" try: headers = {"User-Agent": "Mozilla/5.0"} resp = requests.get(url, headers=headers, timeout=5, verify=False) ``` ### Technical Analysis Both HTTPS requests explicitly set `verify=False`. This disables validation of the server certificate and prevents the client from confirming that it is communicating with the authentic CafeF or TCBS server. The global suppression of `InsecureRequestWarning` further conceals the unsafe configuration from operators. HTTPS encryption without certificate authentication does not prevent an active network attacker from impersonating either remote service. The returned data is parsed and presented as trusted stock information or news. Therefore, a successful interception can compromise the integrity of information supplied to both the Agent and the end user. ### Attack Path 1. An attacker gains a network interception position, such as control of a malicious access point, compromised proxy, DNS infrastructure, or another route between the scanner and the external service. 2. The attacker redirects or intercepts a request to CafeF or TCBS. 3. The attacker presents an arbitrary or self-signed TLS certificate. 4. Because `verify=Fa ...[truncated 1291 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `verify=False` from both `requests.get` calls and rely on Requests' default certificate validation: ```python resp = requests.get(url, headers=headers, timeout=10) ``` ```python resp = requests.get(url, headers=headers, timeout=5) ``` 2. Remove the global suppression of `InsecureRequestWarning`: ```python urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) ``` 3. Ensure the host has a current operating-system CA bundle and keep the Requests and certificate packages updated. 4. If a private certificate authority is genuinely required, supply a narrowly scoped trusted CA bundle through the `verify` parameter rather than disabling validation. 5. Call `resp.raise_for_status()` before parsing response content. 6. Validate response content types, schemas, and expected fields before presenting remote data as successful. 7. Treat all remote titles, links, and API values as untrusted data when incorporating them into subsequent Agent responses. ]]>
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (6)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill instructs the agent to use an `exec` tool to run a local Python script that fetches stock/news data, which implies code execution and likely network access, but the manifest does not declare any explicit tool scope or allowed tools. This creates an authorization and review gap: the skill appears less privileged than it actually is, making it easier to deploy or invoke with capabilities that were not clearly documented or constrained.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
All user-facing natural-language instructions are written as if the skill operates only in Vietnamese, and the description frames it as a Vietnam-stock expert without stating whether language choice is optional. Under the policy, forcing a specific language or locale without user opt-in is a natural-language policy concern unless the constraint is explicitly documented and justified.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The trigger phrases for launching the news scan are very broad, such as generic requests for hot stock news, and the skill also allows empty keywords for general news. In a shared agent environment, this can cause the skill to activate on ordinary market conversation and automatically invoke external data collection or code execution when the user may only want discussion, increasing unintended tool use and data exposure.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
url = "https://cafef.vn/tin-tuc-su-kien.rss"
    try:
        headers = {"User-Agent": "Mozilla/5.0"}
        resp = requests.get(url, headers=headers, timeout=10, verify=False)
        root = ET.fromstring(resp.content)
        
        results = []
Confidence
99% confidence
Finding
The request to CafeF explicitly disables TLS certificate verification with verify=False and globally suppresses the resulting warnings. This allows a man-in-the-middle attacker on the network path to intercept or modify the RSS feed, causing the skill to return spoofed stock news and links that could mislead users or support phishing/social-engineering outcomes.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
url = f"https://apipubaws.tcbs.com.vn/tcanalysis/v1/ticker/{ticker.upper()}/overview"
    try:
        headers = {"User-Agent": "Mozilla/5.0"}
        resp = requests.get(url, headers=headers, timeout=5, verify=False)
        if resp.status_code == 200:
            data = resp.json()
Confidence
99% confidence
Finding
The TCBS API call also disables TLS certificate verification, making the returned ticker overview data vulnerable to interception or tampering by an attacker on the network path. In this skill's stock-analysis context, manipulated financial metrics such as P/E, P/B, EPS, or market cap could directly influence user investment decisions, increasing the practical risk of integrity attacks.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
Natural-language strings throughout the file, including docstrings, CLI descriptions, help text, and error messages, are exclusively in Vietnamese. This can violate language/locale policy when a skill forces a specific language without user opt-in or clear justification.

Static analysis

No suspicious patterns detected.