T09 · Insecure Skill Coding Practices
- Location
monitor.py:565- Finding
Unauthenticated State-Changing Web Endpoints and CSRF Exposure
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill appears to be a real QMT stock-data downloader, but its local web dashboard and installation path expose users to avoidable control-plane and supply-chain risks.
Install only if you are comfortable running a local QMT-connected dashboard that can start, stop, and reconfigure downloads. Keep it bound to localhost, do not expose it on a network, avoid opening it while browsing untrusted sites, verify all external resource files and the xtquant source, and prefer pinned dependencies and local-only frontend assets before production use.
monitor.py:565Unauthenticated State-Changing Web Endpoints and CSRF Exposure
templates/index.html:882DOM-Based Stored Cross-Site Scripting in Dashboard State Rendering
暂无错误
templates/index.html:623Runtime Retrieval and Execution of Remote Frontend Code Without Integrity Verification
requirements.txt:1Unpinned and Incomplete Python Dependency Specification
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
# 有管道输入
instruction = sys.stdin.read().strip()
if instruction:
return instruction
return None
def main():
This duplicate TM1 finding refers to the same unsafe shell-based launch at line 302. The main risk is unnecessary shell invocation for starting a local executable, which widens the attack surface without providing a legitimate benefit.
# 假设 QMT 安装路径在配置中或固定
qmt_path = r"C:\Program Files\XtMiniQmt\XtMiniQmt.exe" # 需根据实际路径配置
try:
subprocess.Popen([qmt_path], shell=True)
print(f"[QMT] 已启动: {qmt_path}")
except Exception as e:
print(f"[ERROR] 启动 QMT 失败: {e}")
This duplicate TM1 finding refers to the same unsafe shell-based launch at line 302. The main risk is unnecessary shell invocation for starting a local executable, which widens the attack surface without providing a legitimate benefit.
# 假设 QMT 安装路径在配置中或固定
qmt_path = r"C:\Program Files\XtMiniQmt\XtMiniQmt.exe" # 需根据实际路径配置
try:
subprocess.Popen([qmt_path], shell=True)
print(f"[QMT] 已启动: {qmt_path}")
except Exception as e:
print(f"[ERROR] 启动 QMT 失败: {e}")
This duplicate TM1 finding refers to the config-driven shell launch at line 333. Because the program also exposes web endpoints that modify configuration, the surrounding skill context makes this more dangerous than a standalone local script: unsafe config trust can chain into process execution.
print(f"[QMT] 正在启动: {exe_path}")
try:
# 使用 subprocess 启动,不等待完成
subprocess.Popen(
[exe_path],
cwd=qmt_path,
shell=True,
This duplicate TM1 finding refers to the config-driven shell launch at line 333. Because the program also exposes web endpoints that modify configuration, the surrounding skill context makes this more dangerous than a standalone local script: unsafe config trust can chain into process execution.
print(f"[QMT] 正在启动: {exe_path}")
try:
# 使用 subprocess 启动,不等待完成
subprocess.Popen(
[exe_path],
cwd=qmt_path,
shell=True,
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="source-map" content="disable"> <!-- 禁用source map以减少控制台警告 -->
<title>股票数据下载监控</title>
<!-- Bootstrap 5 CSS with inline fallback -->
The trigger phrases include very generic requests such as “下载交易数据”, “获取历史数据”, and “下载历史数据”, which are likely to match ordinary user queries outside the intended narrow skill scope. This can cause the agent to invoke the skill unexpectedly, leading to over-broad activation, unintended tool usage, and possible exposure to risky side effects such as launching local software, downloading data, or modifying runtime state without sufficiently specific user intent.
This code emits user-facing logs, errors, progress, and completion messages entirely in Chinese across the skill, such as missing-file notices, progress updates, and task completion text. Because the file does not offer any user language selection or indicate that the skill is intentionally limited to Chinese-speaking users, it violates the language/locale policy for natural-language behavior.
The command-line help text says '下载股票日线数据' (download stock daily-line data), which implies a narrow function limited to stock daily data. However, the script accepts other periods such as 1m/5m/tick via the --period argument and, in download_all_data, processes both stock and index lists rather than stocks only.
No manifest is available, so the skill's purpose is unknown. This file implements del_file, which permanently deletes arbitrary filesystem paths via os.remove, a destructive capability that is not justified by any stated purpose in the available documentation/context.
The del_file function deletes an arbitrary file path via os.remove, but there is no confirmation prompt, docstring, or user-facing disclosure before the destructive operation. The only message appears after failure, so successful deletion happens silently.
When overwrite=True, save_csv unconditionally calls del_file and then writes a new CSV, which can destroy existing user data. There is no visible warning, confirmation, or explanatory documentation indicating that existing files will be replaced.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print(f"[重要] 服务器将运行在端口 {selected_port},请访问 http://127.0.0.1:{selected_port}")
# 运行monitor.py
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print(f"[错误] 网页监控模式启动失败: {e}")
The file contains user-facing logs, errors, help text, and status messages written exclusively in Chinese, indicating a fixed language/locale experience. There is no evidence in this file of user opt-in, locale selection, or documentation justifying a Chinese-only constraint.
The docstring for check_idle_and_shutdown describes graceful server shutdown behavior, but the implementation sets shutdown flags and then calls os._exit(0), which immediately terminates the process without normal cleanup. That is an intent-level contradiction because '关闭服务器' implies stopping the server, not unconditionally killing the whole process.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
return True
else:
# 使用 tasklist 命令
result = subprocess.run(['tasklist', '/FI', 'IMAGENAME eq XtMiniQmt.exe'],
capture_output=True, text=True)
if 'XtMiniQmt.exe' in result.stdout:
return True
This code launches an executable via subprocess.Popen with shell=True, which routes execution through the system shell. Even though the current path is hardcoded in this duplicate definition, using shell=True for process launch is unsafe and can become exploitable if the path or environment is influenced, especially on Windows where shell parsing and executable resolution can be abused.
# 假设 QMT 安装路径在配置中或固定
qmt_path = r"C:\Program Files\XtMiniQmt\XtMiniQmt.exe" # 需根据实际路径配置
try:
subprocess.Popen([qmt_path], shell=True)
print(f"[QMT] 已启动: {qmt_path}")
except Exception as e:
print(f"[ERROR] 启动 QMT 失败: {e}")
This process launch uses shell=True with exe_path and cwd derived from config.json, so a local attacker or any mechanism that can alter that config can influence what the shell executes. In this skill, the danger is increased because the application itself writes config.json and later trusts its contents to launch an external program, creating a clear path to arbitrary code execution under the monitor's privileges.
print(f"[QMT] 正在启动: {exe_path}")
try:
# 使用 subprocess 启动,不等待完成
subprocess.Popen(
[exe_path],
cwd=qmt_path,
shell=True,
This code rewrites config.json based on command-line arguments, but the write is only implied by implementation and lacks any confirmation prompt or explicit warning before modifying local files. Although a success message is printed afterward, there is no prior disclosure to the user that invoking this path will persist changes to disk.
Setting cors_allowed_origins="*" allows any origin to interact with the Socket.IO service from a browser, weakening origin-based protections. In this file, that risk is amplified because the service exposes state-changing HTTP endpoints and real-time control/status functionality, so a malicious website could potentially drive user actions or pair with other weaknesses if the server is reachable.
# 优化Socket.IO配置:禁用调试日志,减少启动开销
socketio = SocketIO(
app,
cors_allowed_origins="*",
logger=False, # 禁用Socket.IO日志
engineio_logger=False, # 禁用Engine.IO日志
ping_timeout=60,
The web API accepts user-supplied settings and persists them to config.json without any authentication, authorization, or CSRF protection visible in this file. In the context of this skill, this becomes more dangerous because configuration is later reloaded and used to control application behavior, including external process launch paths elsewhere in the program.
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e},i=t=>{const i=e(t);return i&&document.querySelector(i)?i:null},n=t=>{const i=e(t);return i?document.querySelector(i):null},s=e=>{e.dispatchEvent(new Event(t))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,a=(t,e,i)=>{Object.keys(i).forEach((n=>{const s=i[n],r=e[n],a=r&&o(r)?"element":null==(l=r)?`${l}`:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(s).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${s}".`)}))},l=t=>!(!o(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t:null},p=[],m=()=>"rtl"===document.documentElement.dir,
...[truncated 27 chars]
This local monitoring UI silently falls back to third-party CDNs for CSS if local assets fail, causing the browser to make external requests and trust remotely hosted code or styling. In an internal/admin dashboard context, that expands the trust boundary, leaks client metadata/IPs, and introduces supply-chain risk if the CDN asset is replaced or tampered with.
The CDN fallback occurs without any user-facing notice, so operators may believe the dashboard is purely local while it is actually contacting external services. That reduces informed consent and can violate privacy, network-segmentation, or compliance expectations for an internal monitoring tool.
The page dynamically injects Socket.IO and Bootstrap JavaScript from external CDNs when local files fail. Unlike CSS, remote JavaScript executes with full page privileges, so a compromised CDN or network path could run arbitrary script in the monitoring interface, access live state, invoke control actions, and interact with internal APIs.
No suspicious patterns detected.