T08 · Insecure Dependencies
- Location
install.py:16- Finding
Automatic Installation of Unpinned Third-Party Dependencies
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears intended to monitor emotion from biometric sensors, but it auto-starts continuous monitoring and a local service with weak user controls and risky install/runtime behavior.
Install only if you intentionally want continuous local biometric/emotion monitoring. Review the code first, pin dependencies or install them in an isolated environment, and avoid running it with elevated privileges. Be aware that local processes can query the emotion API on 127.0.0.1:8766 and that stop/start may kill unrelated matching processes.
install.py:16Automatic Installation of Unpinned Third-Party Dependencies
emopad_cli.py:202Shell Command Injection Through Environment-Influenced Snapshot Path
install.py:65Overbroad Forced Termination of Unrelated Processes
The core declared purpose—continuous PAD estimation from EEG/PPG/GSR data with snapshot generation—is broadly supported by the code. The service does auto-start on FastAPI startup, connects to BLE EEG and serial sensors, computes PAD, and provides snapshot output. However, several materially declared behaviors are not present in the supplied code: there is no popup window logic, no 5-minute periodic display mechanism, and no Linux/Windows viewer invocation despite those being emphasized in the description. Instead, the code only generates images off-screen and exposes them through an HTTP /snapshot endpoint. Also, the listed CLI commands are not implemented in this chunk. Because the description prominently claims a user-facing popup/chart display capability and cross-platform viewer behavior that the code does not perform, this is a description-behavior mismatch.
The description overstates what this specific code chunk does. The code is limited to command-line orchestration of a separate local service over HTTP plus process management. It does align partially with declared commands like status, snapshot, stop, and start, and it does invoke dependency installation support. However, several prominent claims are not represented here: Windows support is contradicted by Linux-specific process/image-launch commands; periodic real-time emoNebula popup behavior is absent; automatic startup on install is not implemented in this file; and hardware support is not present in this chunk. Because the declared purpose presents broader, cross-platform monitoring functionality than the code actually implements, this is a description-behavior mismatch.
The code does implement part of the declared emoNebula behavior: it periodically generates/displays an emotion nebula image approximately every 5 minutes by calling local /pad and /snapshot endpoints, and skips reporting when insufficient sensor validity is indicated. However, several material claims in the description are not represented by this code. Most notably, the implementation is not cross-platform: it hardcodes Linux/X11-specific environment variables and invokes eog directly, with no Windows handling. It also does not communicate with supported hardware devices directly or perform PAD computation itself; instead, it depends on an external local service at 127.0.0.1:8766. Auto-start, dependency installation, and CLI command support are also absent from this code chunk. Therefore the description overstates and mischaracterizes what this supplied code actually does.
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.
f.write(response.content)
print(f"✅ 情绪星云图已保存: {output_path}")
# 自动打开图片
os.system(f"xdg-open {output_path} &")
return 0
else:
print(f"生成截图失败: HTTP {response.status_code}")
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
stderr=subprocess.DEVNULL
)
else:
env = os.environ.copy()
env['DISPLAY'] = ':1'
env['XAUTHORITY'] = '/run/user/1000/gdm/Xauthority'
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.
The manifest explicitly states the skill will auto-start and generate periodic emotion reports, which implies continuous monitoring of highly sensitive biometric and emotional-state data. Without clear consent, disclosure of what is collected/sent/stored, and user controls, this creates a significant privacy and surveillance risk that could expose intimate mental-state information or normalize covert monitoring.
The README states that installation will automatically start a local service and a recurring emotion-reporting feature every 5 minutes, but it does not clearly explain consent, what data is collected, whether any data leaves the device, or how users can disable the behavior before installation. Because the skill processes highly sensitive biometric and emotional-state data, silent background monitoring/report generation creates a meaningful privacy and informed-consent risk even if the service only binds to localhost.
The skill advertises behavior that implies broad capabilities—automatic dependency installation, service startup, local HTTP service use, popup display, and hardware interaction—yet it does not declare any explicit tool scope or permission boundaries. That creates unnecessary ambiguity around what the skill may access or execute, increasing the risk of over-privileged operation if the runtime grants defaults or inferred capabilities.
The skill explicitly states that it will auto-install dependencies, auto-start a local service, and trigger recurring popup windows, but it does so without a prominent consent or warning mechanism. These system-impacting actions can alter host state, consume resources, and surprise users, which is dangerous in agent ecosystems where installation may be assumed to be low impact.
This manifest file contains user-facing natural-language descriptions exclusively in Chinese, such as the field descriptions for serial port, baudrate, and service settings. Under the language/locale policy, forcing a specific language without opt-in or documented justification is a policy concern because users who do not read Chinese may be unable to understand the configuration safely.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
("2-byte start", self.START_COMMAND_2BYTE),
# ("1-byte start", self.START_COMMAND_1BYTE), # 如果2-byte有效可省略
]
max_write = self._control_char.max_write_without_response_size
logger.info(f"Control characteristic max write length: {max_write}")
for name, cmd in commands:
The FastAPI service exposes current emotional state, modality validity, history, and generated snapshots over HTTP without authentication or explicit access controls. Although it binds to 127.0.0.1, any local process running as the user or able to reach loopback can query sensitive biometric-derived data, which is especially sensitive given this skill auto-starts and continuously monitors emotions.
The module docstring, CLI description, help text, and runtime messages are presented in Chinese, with no indication of locale selection or user opt-in. This creates a language-policy issue because the skill imposes a specific language on all users rather than allowing a choice or documenting a justified locale restriction.
The code force-kills any process whose command line matches a broad regex for 'python3.*nebula.py'. This can terminate unrelated user or system processes that happen to match, causing denial of service and unsafe cross-process interference beyond the skill's own resources.
"""关闭所有 nebula 和 eog 进程"""
# 杀死所有 nebula.py 进程
try:
subprocess.run(['pkill', '-9', '-f', 'python3.*nebula.py'],
capture_output=True, timeout=2)
except:
pass
The stop path uses destructive, broad-spectrum process termination without confirmation, targeting regex matches rather than verified skill-owned processes. In the context of an auto-starting background service, this makes accidental or repeated disruption more likely and can impact unrelated local applications.
The code force-kills any eog process matching 'eog.*nebula_latest', which may affect unrelated image viewer sessions if their arguments match. Using SIGKILL with broad process-name matching makes termination immediate and bypasses graceful shutdown, increasing the risk of disruption or data loss.
# 杀死所有 eog 进程(显示 nebula 图片的)
try:
subprocess.run(['pkill', '-9', '-f', 'eog.*nebula_latest'],
capture_output=True, timeout=2)
except:
pass
The CLI's stated purpose is service control, but it also triggers dependency installation by executing another script. This broadens the trust boundary and creates a stealthy execution path during normal use, which is especially risky because the skill metadata says dependencies install automatically.
The CLI automatically executes a separate installer script during service startup, expanding behavior from control operations into arbitrary code execution from a colocated file. If the skill directory or install.py is modified, startup becomes a code-execution path with the user's privileges and no explicit consent at that moment.
if os.path.exists(install_script):
print("正在检查并安装依赖...")
result = subprocess.run([sys.executable, install_script])
return result.returncode == 0
return False
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print("🚀 启动 emoPAD Universe 服务...")
process = subprocess.Popen(
[sys.executable, service_script],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
time.sleep(3)
try:
response = requests.get(f"http://127.0.0.1:8766/pad", timeout=5)
if response.status_code == 200:
with open(PID_FILE, 'w') as f:
f.write(str(process.pid))
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
time.sleep(3)
try:
response = requests.get(f"http://127.0.0.1:8766/pad", timeout=5)
if response.status_code == 200:
with open(PID_FILE, 'w') as f:
f.write(str(process.pid))
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
time.sleep(3)
try:
response = requests.get(f"http://127.0.0.1:8766/pad", timeout=5)
if response.status_code == 200:
with open(PID_FILE, 'w') as f:
f.write(str(process.pid))
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
time.sleep(3)
try:
response = requests.get(f"http://127.0.0.1:8766/pad", timeout=5)
if response.status_code == 200:
with open(PID_FILE, 'w') as f:
f.write(str(process.pid))
The installer both installs dependencies and starts long-running services automatically without a confirmation gate. That behavior is security-relevant because it causes immediate code execution, persistence-like background activity, and hardware interaction at install time, all of which exceed many users' expectations for a skill install.
No suspicious patterns detected.