T09 · Insecure Skill Coding Practices
- Location
wojiayun_skill/key_manager.py:111- Finding
Hard-Coded API Credential Distributed in Source Code
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill mostly matches a Wojiayun device and work-order API client, but it ships unsafe credential handling and hidden high-impact side effects that need review before use.
Review this skill carefully before installing. Use only a revocable, least-privilege Wojiayun API key; assume the embedded default key is compromised and should not be trusted. Do not use it where arbitrary base_url changes, local credential files, SQL exports, or the production obfuscation build could expose or damage business data. Prefer a version that removes the hardcoded key, pins the API host, uses proper secret storage, documents uploads and bulk exports, and separates developer build tooling from runtime behavior.
wojiayun_skill/key_manager.py:111Hard-Coded API Credential Distributed in Source Code
wojiayun_skill/token_manager.py:107API Key Exposure Through a GET Query String and Mutable Authentication Destination
wojiayun_skill/crypto_utils.py:17Predictable At-Rest Encryption and Missing Credential-File Permission Controls
wojiayun_skill/mode_manager.py:63Runtime Mode Change Silently Triggers Destructive Obfuscation Build
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.
# Wojiayun Skill - 我家云设备管理与工单系统
The module decrypts and returns stored API keys, which is a sensitive credential access operation, but provides no user-facing disclosure when this occurs. In addition, the test initializer embeds and stores an API key value directly, increasing the sensitivity of the operation without any warning to users.
The function init_default_key() writes a hardcoded API key-like secret to persistent storage, which creates an embedded credential in source code and on disk. Even if labeled as 'for testing', hardcoded secrets are commonly leaked through source control, packaging, logs, backups, or accidental execution in production-like environments.
get_system_prompt() returns the plaintext system prompt directly to any caller of the wrapper, which defeats the stated goal of preventing users from viewing protected prompts if this object is reachable from less-trusted code. In an agent skill context, exposing the system prompt can reveal hidden instructions, safety logic, tool-routing behavior, or other sensitive prompt content that attackers can use for prompt extraction or bypass attempts.
if not prompt:
# 默认提示词
prompt = "你是一个专业的设备管理助手。"
return prompt
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
Access Token 管理模块
实现 token 失效检测和自动刷新
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
Access Token 管理模块
实现 token 失效检测和自动刷新
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
Access Token 管理模块
实现 token 失效检测和自动刷新
"""
Suspicious Unicode normalization or mixed-script content
The skill documents a file upload capability but does not warn users that files will be transmitted to a remote service, nor does it explain privacy, retention, or handling implications. In a device/work-order management context, uploads may contain photos, videos, or documents with sensitive operational or personal data, so omission of this notice can lead to unintended data disclosure.
The module docstring at L01-L03 presents the skill as a 'device data analysis' skill, which implies an analytics-focused purpose. However, the package exports functions and classes for API key storage, encryption/decryption, secure API access, prompt management, and token expiration handling, which materially extend beyond simple data analysis and make the documentation misleading about the skill's actual capabilities.
The top-level documentation states the module uses an AES symmetric encryption algorithm to protect sensitive data. However, the code constructs a cryptography.fernet.Fernet instance and performs encryption/decryption through Fernet tokens, which is a different API and documented abstraction than the stated implementation.
This file’s natural-language interface elements, including the module docstring, class docstring, and parameter documentation, are entirely in Chinese. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation, and no alternative language or opt-in mechanism is documented here.
The skill is designed to automatically retrieve all device data across all pages and store it in memory, but it provides no user-facing warning, consent step, scoping control, or sensitivity check before performing bulk collection. In an equipment-management context, full device inventories can expose operational details, locations, maintenance assignments, and other sensitive organizational data that increase reconnaissance and data-leak risk.
The skill’s stated purpose is device data fetching and statistical analysis, but it also includes a bulk export_to_sql() function that writes all retrieved equipment records to a local SQL file. This materially expands the data-handling surface and enables easy persistence and exfiltration of potentially sensitive asset inventory data without any access scoping, minimization, confirmation, or safeguards.
This code writes encrypted key metadata to a fixed local file path, and related logic later deletes stored keys, but there is no confirmation prompt or user-facing notice around these state-changing file operations. Although docstrings describe the functions, they do not serve as a clear user disclosure that secret material will be persisted to disk at a specific location.
The module docstring and class docstring both describe the component as '运行环境配置' (runtime environment configuration), implying simple mode/config state management. However, calling apply("B") invokes _build_production(), which imports an obfuscator and builds a production/obfuscated version of code, a materially different side effect not reflected in the documentation.
In a file presented and documented as a runtime profile/configuration manager, the apply method does more than persist mode state: it conditionally triggers a production build via an obfuscator. With no manifest available and no stated purpose beyond runtime configuration in code docs, bundling build/obfuscation functionality into this component is an unjustified extra capability.
The apply() method writes to a persistent config file and, when switching to production mode, automatically invokes a build operation. While the code comments describe the behavior, there is no user-facing disclosure such as a prompt, print/log message, or other visible warning before these state-changing actions occur.
The code writes a Python setup script to disk and then executes it, creating a code-execution boundary based on generated file content and filesystem paths. If an attacker can influence source_file, output_dir, or the build environment, they may be able to trigger unintended code execution or overwrite files, and the absence of safety restrictions increases the blast radius in compromised or multi-user environments.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# 执行编译
import subprocess
result = subprocess.run(
[sys.executable, setup_file],
capture_output=True,
text=True
The production build unconditionally deletes and recreates a fixed absolute directory, which can cause destructive data loss if the path is misconfigured, reused, or points to unexpected content. Because OBF_DIR is hard-coded and shutil.rmtree is called without confirmation, safety checks, or path validation, a user running this tool can erase files outside the intended build context.
The module advertises itself as a secure configuration manager, but it also contains active network request execution and token-refresh behavior. This mismatch can mislead downstream users or reviewers into granting the code more trust than warranted, reducing scrutiny over outbound communications and authenticated actions.
The client performs live outbound requests and can send authorization headers, request data, and API-key-derived token refresh traffic without any explicit user-facing disclosure or consent gate. In a skill context, this can cause silent transmission of sensitive credentials or business data to remote services, especially because token refresh is automatic.
The code issues outbound POST requests to decrypted URLs with caller-supplied JSON bodies and headers, including authorization data. Even though the base URL is preset and uses HTTPS by default, the file also allows updating the base URL and automatically refreshes tokens, increasing the risk of sensitive data transmission to unintended endpoints if configuration is altered or abused.
if method.upper() == "GET":
response = requests.get(url, headers=headers, timeout=30)
elif method.upper() == "POST":
response = requests.post(url, json=data, headers=headers, timeout=30)
else:
return {"error": f"不支持的 HTTP 方法: {method}"}
The module claims prompts are protected from viewing and tampering, but this file provides only confidentiality-by-encryption and no integrity protection or access control. Any code with access to this class can retrieve decrypted prompts, and the on-disk JSON can be modified without any signature, MAC, or authenticated-encryption verification to detect tampering.
Detected: suspicious.exposed_secret_literal