Back to skill

Security audit

Local LRC Editor 专业LRC歌词创作工具

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real LRC lyric editor, but its unauthenticated network service exposes unsafe upload handling and a shutdown endpoint.

Review carefully before installing. Run it only in a trusted local environment, preferably bound to localhost rather than all interfaces, and avoid using it on shared or privileged accounts until the upload path handling, shutdown route, dependency pinning, and resource limits are fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
web/app.py:42
Finding
Arbitrary File Overwrite and Deletion Through Unsanitized Upload Filename<![CDATA[ ## Vulnerability Details **File Location**: `web/app.py:42-59` **Vulnerability Type**: Path traversal and unsafe temporary-file handling **Risk Level**: Critical ### Vulnerable Code ```python @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return jsonify({'error': '没有选择文件'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': '没有选择文件'}), 400 if file and allowed_file(file.filename): # 保存临时文件 temp_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) file.save(temp_path) # 生成波形数据 waveform = get_waveform_data(temp_path) # 删除临时文件 os.unlink(temp_path) return jsonify(waveform) return jsonify({'error': '不支持的文件格式'}), 400 ``` ### Technical Analysis The multipart filename is controlled by the requester and is used directly as a filesystem path. The application neither applies `werkzeug.utils.secure_filename` nor generates a server-controlled temporary filename. `os.path.join()` does not guarantee that the resulting path remains under `UPLOAD_FOLDER`. A filename containing parent-directory components can escape the temporary directory. On platforms where an absolute path is accepted as the uploaded filename, the absolute component can also replace the configured temporary directory entirely. The extension allowlist does not prevent the vulnerability. An attacker only needs to target a writable path whose final extension is one of `mp3`, `wav`, `flac`, `ogg`, or `m4a`. After saving attacker-controlled content to that path, the application unconditionally calls `os.unlink(temp_path)`. Audio parsing failures are caught inside `get_waveform_data()`, so an invalid audio payload does not prevent the subsequent deletion. The use of a predictable, client-selected temporary path also permits filename collisions and creates symlink-related risks in a shared temporary directory. ### ...[truncated 1382 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never derive a server-side path from the submitted filename. - Create a unique server-controlled file with `tempfile.NamedTemporaryFile`, `tempfile.mkstemp`, or an equivalent safe API. - Preserve the original filename only as non-path metadata. - If a filename must be retained, apply `secure_filename()` and verify with `os.path.realpath()` that the final path remains inside a dedicated, private upload directory. - Reject absolute paths, path separators, parent-directory components, and empty normalized filenames. - Open temporary files using exclusive creation semantics to prevent collisions. - Do not use a shared predictable path in the system-wide temporary directory. - Delete the generated temporary file in a `finally` block, but only after confirming that it is the exact server-created file. - Run the service under a dedicated low-privilege account with access only to required directories. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
web/app.py:63
Finding
Unauthenticated Network-Accessible Service Shutdown<![CDATA[ ## Vulnerability Details **File Location**: `web/app.py:63-80` **Vulnerability Type**: Missing authentication and authorization on a destructive endpoint **Risk Level**: High ### Vulnerable Code ```python @app.route('/shutdown', methods=['POST']) def shutdown(): """关闭服务""" try: import sys os.kill(os.getpid(), signal.SIGTERM) return jsonify({'status': 'success', 'message': '服务已关闭'}) except: try: # Windows兼容退出 os._exit(0) except: return jsonify({'status': 'error', 'message': '关闭失败,请手动停止服务'}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=698, debug=False) ``` ### Technical Analysis The `/shutdown` endpoint terminates the server process without authenticating or authorizing the requester. The Flask development server is bound to `0.0.0.0`, making the endpoint available on every network interface rather than only on the local loopback interface described in the documentation. Requiring the `POST` method is not an adequate security boundary. There is no CSRF token, origin validation, secret shutdown token, session authentication, or network-level authorization. A malicious website may also attempt a simple cross-origin form submission to the endpoint even though browser same-origin policy prevents reading the response. The broad exception handler falls back to `os._exit(0)`, ensuring abrupt process termination if the normal signal-based path raises an exception. ### Attack Path 1. The attacker discovers or otherwise reaches TCP port 698 on the host. 2. The attacker sends an unauthenticated `POST` request to `/shutdown`. 3. The route calls `os.kill(os.getpid(), signal.SIGTERM)`. 4. If that operation fails, the exception path calls `os._exit(0)`. 5. The Flask process terminates, interrupting every active user of the application. A browser-based attack can attempt the same action by submitting a cross-origin HTML form to `http://target:698/shut ...[truncated 479 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the HTTP shutdown endpoint and require the operator to terminate the process through the local terminal or service manager. - Bind this local utility to `127.0.0.1` or `::1` rather than `0.0.0.0`. - If remote shutdown is an explicit requirement, require strong authentication and narrowly scoped authorization. - Add CSRF protection and validate the `Origin` or `Referer` header for browser-driven administrative actions. - Use a cryptographically random, short-lived administrative token rather than a hardcoded secret. - Restrict port 698 with host firewall rules. - Avoid `os._exit()` in request handlers because it bypasses normal cleanup and graceful shutdown. - Deploy behind a production WSGI server if network exposure is required. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
web/app.py:22
Finding
Unauthenticated Audio Processing Enables Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `web/app.py:13, 22-34, 42-59, 80` **Vulnerability Type**: Unbounded decoded-media processing and denial of service **Risk Level**: High ### Vulnerable Code ```python app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 最大100MB ``` ```python def get_waveform_data(file_path, num_points=1000): """生成音频波形数据""" try: audio = AudioSegment.from_file(file_path) samples = np.array(audio.get_array_of_samples()) # 合并声道 if audio.channels == 2: samples = samples.reshape((-1, 2)).mean(axis=1) # 采样到指定点数 if len(samples) > num_points: samples = samples[::len(samples)//num_points][:num_points] # 归一化 samples = samples / np.max(np.abs(samples)) return { 'duration': audio.duration_seconds * 1000, 'waveform': samples.tolist(), 'sample_rate': audio.frame_rate } except Exception as e: return {'error': str(e)} ``` ```python @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return jsonify({'error': '没有选择文件'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': '没有选择文件'}), 400 if file and allowed_file(file.filename): temp_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) file.save(temp_path) waveform = get_waveform_data(temp_path) os.unlink(temp_path) return jsonify(waveform) return jsonify({'error': '不支持的文件格式'}), 400 ``` ```python app.run(host='0.0.0.0', port=698, debug=False) ``` ### Technical Analysis The 100 MB request limit controls only the encoded upload size. It does not constrain decoded duration, sample count, channel count, sample width, memory expansion, FFmpeg processing time, or concurrent requests. `AudioSegment.from_file()` decodes the uploaded media, after which `get_array_of_samples()` and ` ...[truncated 1514 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Bind the utility to loopback unless network access is explicitly required. - Require authentication before accepting uploads. - Apply per-client request-rate limits and global concurrency limits. - Enforce a substantially smaller encoded upload limit appropriate for the intended use. - Inspect media metadata in a constrained process and reject excessive duration, sample rate, channel count, or decoded size before full processing. - Execute media decoding in an isolated worker with strict CPU, memory, file-size, process-count, and wall-clock limits. - Add decoder timeouts and terminate stalled FFmpeg processes. - Stream or incrementally downsample audio instead of materializing the complete decoded sample array. - Place temporary uploads in a quota-controlled directory and guarantee cleanup on all error paths. - Use a production WSGI server and reverse proxy configured with request, connection, and timeout limits. ]]>

T08 · Insecure Dependencies

Warning
Location
start_server.py:6
Finding
Mutable Dependencies Are Installed Automatically at Runtime<![CDATA[ ## Vulnerability Details **File Location**: `start_server.py:6-26` and `web/requirements.txt:1-3` **Vulnerability Type**: Unpinned runtime dependency installation **Risk Level**: Medium ### Vulnerable Code ```python def install_package(package): """安装Python包""" subprocess.check_call([sys.executable, "-m", "pip", "install", package]) def check_dependencies(): """检查并安装依赖""" required_packages = [ "flask", "pydub", "numpy" ] print("检查依赖中...") for package in required_packages: try: importlib.import_module(package) print(f"{package} 已安装") except ImportError: print(f"正在安装 {package}...") install_package(package) print("所有依赖安装完成") ``` ```text flask>=2.3.0 pydub>=0.25.1 numpy>=1.24.0 ``` ### Technical Analysis When a required module is unavailable, application startup invokes pip and installs a package by its bare name. No exact version, package hash, lockfile, trusted index URL, or artifact signature is enforced. The startup script does not use `web/requirements.txt`; therefore, even its minimum-version constraints do not govern the actual automatic installation. The constraints themselves are open-ended and permit future versions that were not part of this audit. Pip uses the environment's configured package indexes and can download and execute package installation logic. Consequently, application startup behavior depends on mutable external repository state and local pip configuration rather than a reproducible, reviewed dependency set. ### Attack Path 1. One of the three modules is absent from the Python environment. 2. A user runs `start_server.py`. 3. The script invokes `python -m pip install` with only the package name. 4. Pip resolves the package from its configured index and selects the currently available version. 5. If the index, package release, dependency chain, DNS/proxy configuration, or pip configuration ha ...[truncated 628 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove automatic package installation from application startup. - Build a virtual environment during a separate, explicit deployment step. - Pin every direct and transitive dependency to an exact reviewed version. - Generate a lockfile and require cryptographic hashes, such as with `pip install --require-hashes`. - Configure an explicit trusted package index or an internally controlled artifact repository. - Review and update dependency pins through a controlled maintenance process. - Make `start_server.py` fail with a clear dependency error instead of modifying the environment automatically. - Ensure the deployment process installs from the audited dependency manifest rather than maintaining a separate package-name list. ]]>

T08 · Insecure Dependencies

Warning
Location
web/templates/index.html:7
Finding
Third-Party Browser Scripts Are Loaded Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `web/templates/index.html:7-8` **Vulnerability Type**: Unverified remote frontend dependencies **Risk Level**: Medium ### Vulnerable Code ```html <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/wavesurfer.js@7.7.3/dist/wavesurfer.min.js"></script> ``` ### Technical Analysis The page loads executable JavaScript directly from a third-party CDN. Although explicit package versions and HTTPS URLs are used, the script elements do not specify Subresource Integrity hashes. The application also does not establish a restrictive Content Security Policy in the reviewed code. The browser therefore trusts whatever content the CDN returns for those URLs at page-load time. A compromise of the CDN, package distribution account, or delivery path could alter the effective client-side code without modifying the audited Skill files. Because the scripts execute in the application's origin, injected code would have the same browser privileges as the legitimate frontend. It could read application DOM content and local storage, modify exports, send network requests, or invoke the local `/shutdown` endpoint. ### Attack Path 1. An attacker compromises the CDN response, associated package distribution, or another part of the remote delivery chain. 2. A user opens the application page. 3. The browser requests the two JavaScript resources from the CDN. 4. Because no integrity hash is supplied, the browser executes the returned content without checking it against an audited digest. 5. The modified script executes in the `http://localhost:698` or corresponding service origin. 6. The script accesses locally stored lyrics, alters application behavior, or invokes backend endpoints available to that origin. ### Impact Assessment Successful exploitation grants attacker-supplied JavaScript access to the web application's browser origin. This includes lyrics ...[truncated 357 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer vendoring reviewed JavaScript files with the application and serving them from the local origin. - If CDN delivery is retained, add verified `integrity` attributes and appropriate `crossorigin="anonymous"` attributes. - Recalculate and review integrity hashes only through a controlled dependency-update process. - Add a restrictive Content Security Policy that permits scripts only from required locations and avoids `unsafe-eval`. - Consider using a frontend dependency lockfile and reproducible build process. - Configure security headers such as `Content-Security-Policy`, `X-Content-Type-Options: nosniff`, and an appropriate `Referrer-Policy`. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill description materially overstates implemented functionality while omitting an extra shutdown capability, which is a trust and security problem in agent-distributed skills. Users may expose files, run the service, or rely on data-handling guarantees that do not exist, and the undocumented shutdown endpoint widens the attack surface beyond the declared purpose.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The `/shutdown` route allows any requester to terminate the entire Flask process with no authentication, authorization, or local-only restriction. For an LRC editing service, remote process termination is unnecessary to core functionality and creates an easy denial-of-service condition for anyone who can reach the port.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
Exposing a visible 'close service' control in an LRC editor introduces an operational capability unrelated to the tool's editing purpose. This increases the attack surface by advertising a backend termination path that can be abused for denial of service if the shutdown endpoint is reachable without strong authorization.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The client issues a POST request to /shutdown, enabling backend termination from browser-accessible code. In context, this is dangerous because an LRC editor should not let ordinary page interactions stop the service; if server-side controls are weak, this can become a trivial denial-of-service vector.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill documentation instructs users to run a Python server that can install dependencies and use shell-level execution, but it does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, undocumented shell capability increases the chance of unintended command execution and makes risk review, policy enforcement, and user consent weaker.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
Natural-language policy violations include forcing a specific language without user opt-in. The entire skill description and invocation guidance are presented only in Chinese, and the file does not indicate that the user may choose another language or that the skill is intentionally limited to a Chinese-language audience.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
Automatically installing Python packages on startup exceeds what a user would reasonably expect from an LRC editing tool and causes code from external repositories to be fetched and executed implicitly. This creates a supply-chain attack surface and can alter the host environment without informed consent.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script installs packages via pip without meaningful upfront consent, only emitting status messages as it proceeds. Users may not realize the tool will download and execute third-party package installation logic, which increases risk to the local environment and reduces transparency.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def install_package(package):
    """安装Python包"""
    subprocess.check_call([sys.executable, "-m", "pip", "install", package])

def check_dependencies():
    """检查并安装依赖"""
Confidence
95% confidence
Finding
The script invokes pip in a subprocess to install packages at runtime. While the package names are hard-coded and there is no shell injection here, dynamically modifying the Python environment during startup introduces supply-chain and integrity risk, especially if package indexes, dependency resolution, or network paths are compromised.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The skill is presented as a lyric authoring web tool, but this file uses subprocess execution to run pip and launch another Python process. Spawning subprocesses is a powerful capability that is not mentioned in the manifest and is not inherently part of LRC editing functionality.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("访问地址: http://localhost:698")
    print("按 Ctrl+C 停止服务")
    
    subprocess.run([sys.executable, "app.py"])
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
This endpoint performs a destructive action solely via an unauthenticated POST request, so any local or remote party with network access can stop the service instantly. Even if intended as a convenience feature, exposing process termination through the application interface materially weakens availability and may also be triggerable by cross-site requests if the service is used from a browser.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The page declares zh-CN as the document language and presents the title and interface text in Chinese, with no indication that language selection is optional or region-specific. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The page loads executable JavaScript directly from third-party CDNs, which expands trust beyond the stated local lyric-editing scope. If the CDN, dependency, or delivery path is compromised, arbitrary script can execute in the app context and access local lyric data, browser storage, and UI actions.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code automatically stores the user's lyric data and uploaded file name in browser localStorage, which persists across sessions and may expose creative content or metadata to other users of the same browser profile. There is no visible notice in the UI, comment, or other user-facing disclosure that imported or edited data will be retained locally.

Missing User Warnings

Low
Confidence
91% confidence
Finding
For markdown files, missing-warning findings apply when the description omits warnings about behaviors affecting user data or privacy. The document advertises automatic saving to browser local storage, but only later mentions recovery behavior; it does not upfront warn that edited lyric content remains stored locally on the device/browser.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The script's natural-language strings and comments are consistently in Chinese, including operational output shown to the user. Under the policy, forcing a specific language without opt-in can be a locale/language policy violation unless the restriction is clearly justified or optional.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The file includes Chinese-only natural-language strings in comments, docstrings, and API error/status messages such as '没有选择文件' and '服务已关闭'. This enforces a specific language for user-visible behavior without any opt-in, language selection, or justification that the skill is intentionally region-specific.

Unpinned Dependencies

Low
Category
Supply Chain
Content
flask>=2.3.0
pydub>=0.25.1
numpy>=1.24.0
Confidence
97% confidence
Finding
The dependency is specified with a lower bound only, which allows future installs to resolve to different Flask versions over time. This weakens reproducibility and makes it impossible to verify whether deployed versions include known security fixes or newly introduced vulnerable releases.

Unverifiable Dependency: flask has 10 known advisory(ies) (CVE-2025-47278 (Flask uses fallback key instead of current signing key); CVE-2018-1000656 (Flask is vulnerable to Denial of Service via incorrect encoding of JSON data); CVE-2019-1010083 (Pallets Project Flask is vulnerable to Denial of Service via Unexpected memory u) +7 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding
Flask has known advisories, and because the manifest does not pin an exact version, there is no reliable way to determine whether the installed package is affected. For a Flask-backed web skill exposed on a network port, this uncertainty increases operational risk because deployment may resolve to an insecure version.

Unpinned Dependencies

Low
Category
Supply Chain
Content
flask>=2.3.0
pydub>=0.25.1
numpy>=1.24.0
Confidence
93% confidence
Finding
Using pydub with only a minimum version makes builds non-deterministic and can pull in different versions across environments. While this is primarily a supply-chain hygiene issue, it can expose deployments to unreviewed or vulnerable upstream releases.

Unpinned Dependencies

Low
Category
Supply Chain
Content
flask>=2.3.0
pydub>=0.25.1
numpy>=1.24.0
Confidence
96% confidence
Finding
The unpinned numpy requirement permits installation of any version at or above 1.24.0, preventing reliable verification of security posture and behavior. In practice, this can lead to inconsistent environments and accidental exposure to vulnerable or incompatible releases.

Unverifiable Dependency: numpy has 16 known advisory(ies) (CVE-2014-1859 (Numpy arbitrary file write via symlink attack); CVE-2021-41495 (NumPy NULL Pointer Dereference); CVE-2021-33430 (NumPy Buffer Overflow (Disputed)) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding
NumPy has multiple published advisories, and the unpinned requirement means the actual installed release cannot be verified from this file alone. Although numpy is not typically the primary attack surface in this application, unverifiable versions still create avoidable supply-chain and maintenance risk.

Static analysis

No suspicious patterns detected.