Back to skill

Security audit

Kids Points V2

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-built for family points tracking, but it needs review because it handles credentials and optional local-network services more broadly than the main description makes clear.

Install only if you are comfortable giving this skill access to a local SQLite ledger, an LLM API key, and Feishu message text. Prefer a dedicated skill-scoped API key via an explicit env var, avoid using global ~/.hermes credential stores, restrict KIDS_POINTS_RUNTIME_DIR to trusted code, and run the dashboard only on localhost or a trusted LAN with added access control.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (29)

Tainted flow: 'V2_RUNTIME_DIR' from os.environ.get (line 24, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
# cli.py 走 process_message → 8 步 pipeline → 写 V2 SQLite → 返 result["reply"]
    # V2 的 dedup 走 trace_id, 单进程内 random hex 已足够
    try:
        result = subprocess.run(
            ["python3", "cli.py", message],
            cwd=V2_RUNTIME_DIR,
            capture_output=True,
Confidence
93% confidence
Finding
The subprocess working directory is derived from KIDS_POINTS_RUNTIME_DIR, which is taken directly from the environment and can point to an arbitrary location. Because the command executed is python3 cli.py with that cwd, an attacker who can influence the service environment can cause execution of a different cli.py, leading to arbitrary code execution under the skill's privileges.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill advertises no declared permissions, yet its documented functionality clearly implies access to environment variables, filesystem read/write, networked LLM APIs, and shell execution via python3. This mismatch weakens transparency and security review because users and platforms cannot accurately assess the capability surface before installation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The documented purpose focuses on kids points tracking, SQLite, LLM parsing, and Feishu/CLI use, but the broader project behavior includes HTTP endpoints, WebSocket broadcasting, file watching, and external dashboard/data-serving components not disclosed in the primary description. Hidden or under-disclosed network services materially expand the attack surface and can expose ledger data or create remote interaction paths users did not expect.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The handler launches an external Python process for every incoming message and passes the full parent environment into that subprocess. In this agent context, that can expose API keys and other secrets to the Python runtime and any libraries it loads, increasing blast radius if the runtime is compromised or behaves unexpectedly.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The server exposes multiple unauthenticated endpoints on 0.0.0.0, including health, dashboard data, a WebSocket channel, and a state-changing POST endpoint. In the context of a family points dashboard, this broader remotely reachable surface is unnecessary and allows any reachable host to read internal status and interact with server behavior without access control.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The /api/push endpoint lets any unauthenticated caller remotely change server state by forcing the cache dirty and triggering downstream refresh behavior. An attacker on a reachable network can repeatedly call it to induce extra backend work, create noisy WebSocket broadcasts, and degrade service reliability even if they cannot directly alter the underlying SQLite data.

Intent-Code Divergence

Medium
Confidence
85% confidence
Finding
The CLI presents 'sparse' as a mock mode, but that branch actually calls sim.fetch_data(), which appears to retrieve live data from the production-backed source. In a tuning/debug utility, this can expose real children's points/activity data unexpectedly, causing privacy leakage or accidental use of production data in local screenshots, logs, or test workflows.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
`execute_query` accepts arbitrary SQL and directly passes it to `conn.execute(sql)` while claiming it is read-only and intended for LLM-generated SQL. An attacker or prompt-influenced model could issue mutating statements such as `DELETE`, `UPDATE`, `DROP TABLE`, or PRAGMA/ATTACH-style operations, resulting in data loss, corruption, or broader local file/database abuse depending on SQLite capabilities.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
This module reads credential material from ~/.hermes/auth.json and ~/.hermes/.env even though the skill’s stated purpose is kids-points management, not account-wide credential brokerage. Pulling secrets from unrelated user-global stores expands the skill’s privilege boundary and could let the skill use or expose credentials the user did not intend to share with this application.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The file-level claim that deterministic operations are handled purely by code is contradicted by the query path, where the LLM generates SQL that is later executed. This creates a trust-boundary violation: operators or reviewers may assume the system is safer than it is, while an attacker can influence database queries through prompt injection or model misbehavior.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The handler explicitly supports overriding the runtime directory via an environment variable, and later executes cli.py from that directory. In a plugin or hosted-agent environment, this creates a straightforward code-substitution path if deployment configuration, launch scripts, or inherited environment can be altered.

Context-Inappropriate Capability

Low
Confidence
86% confidence
Finding
The subprocess inherits the full parent environment, including secrets such as API keys noted in the comment. If the executed cli.py is replaced via the runtime-dir override, or if it logs/transmits environment variables, those credentials become exposed to child code.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly instructs users to place the Wi‑Fi password and server host directly in source/config values before flashing the ESP32. That encourages secret hardcoding, which can lead to credential leakage through version control, screenshots, shared firmware projects, or reused config files. In this hardware/IoT context, embedded credentials are especially risky because they are often copied across devices and are harder to rotate once deployed.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
Because this path is explicitly meant for LLM-generated input, arbitrary SQL execution is especially risky in this skill context: the model can be induced by malicious chat content or prompt injection to generate destructive queries. The absence of technical restrictions or even warning barriers means untrusted natural-language input may be transformed directly into database operations with full connection privileges.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code loads ~/.hermes/.env during import, causing sensitive local credentials to be ingested automatically before the user invokes any LLM feature or receives any disclosure. Import-time secret access is dangerous because it is implicit, hard to audit, and increases the chance that unrelated code paths gain access to secrets unexpectedly.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This function sends prompt content to an external LLM API endpoint, which can expose user messages, child-related records, or other sensitive data to a third party if callers pass such content. The lack of user-facing notice or data-handling controls makes accidental privacy leakage more likely in a skill that appears to process personal family information.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The raw debug LLM call also transmits prompt content externally, but without any filtering or JSON extraction safeguards and without user disclosure. Debug paths are especially risky because developers may send verbose internal or sensitive data while assuming the function is only local instrumentation.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
User messages are forwarded verbatim to the LLM for classification/parsing/query generation, which may include children's behavior records, rewards, penalties, and other sensitive household data. Without disclosure, consent, minimization, or redaction, this creates a privacy and data-handling risk, especially given the skill explicitly manages children's records.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The main handler silently forwards the entire environment to a subprocess while processing untrusted chat input. In this skill context, the combination of external-message handling and child-process execution increases the blast radius of any compromise in cli.py or runtime-path tampering, potentially exposing credentials or internal configuration.

Credential Access

High
Category
Privilege Escalation
Content
try {
      py = spawn('python3', args, {
        cwd: V2_RUNTIME_DIR,
        env: { ...process.env },  // 继承 LLM API key 等环境变量
        timeout: 30000,
      });
    } catch (e) {
Confidence
95% confidence
Finding
The code explicitly clones and forwards the entire process environment to the Python subprocess, including LLM API keys and any other credentials present in the agent process. If the Python CLI, its dependencies, or the runtime directory are compromised, those secrets become directly accessible and can be exfiltrated.

Credential Access

High
Category
Privilege Escalation
Content
_PROJECT_ROOT = Path(__file__).parent
_CONFIG_PATH = _PROJECT_ROOT / "config.yaml"
_HERMES_AUTH_PATH = Path.home() / ".hermes" / "auth.json"
_HERMES_ENV_PATH = Path.home() / ".hermes" / ".env"


# ─── ~/.hermes/.env 自动加载 ─────────────────────────────────────────────────
Confidence
94% confidence
Finding
Referencing user-global credential file paths for later consumption gives this skill access to secrets outside its apparent functional scope. In the context of a children’s points management skill, that mismatch makes the behavior more suspicious and increases the blast radius if the module is reused or compromised.

Credential Access

High
Category
Privilege Escalation
Content
_HERMES_ENV_PATH = Path.home() / ".hermes" / ".env"


# ─── ~/.hermes/.env 自动加载 ─────────────────────────────────────────────────
# Hermes 把真实 API key 存在 ~/.hermes/.env;auth.json 的 credential_pool
# 条目通过 source: "env:<YOUR_API_KEY_VAR>" 引用它。本项目代码在普通 shell
# 下不会自动 source 这个文件,所以在 import 时主动把里面声明的 KEY 类变量
Confidence
96% confidence
Finding
The code comment and logic indicate automatic loading of ~/.hermes/.env, meaning arbitrary environment-style secrets from a user-global file may be imported into the process. This is dangerous because it can unintentionally expose unrelated credentials to the application and any imported dependencies.

Credential Access

High
Category
Privilege Escalation
Content
if k and k not in os.environ:
                os.environ[k] = v
    except OSError:
        # .env 读取失败不应让 import 崩溃 — 走原 KeyError 路径更清晰
        pass
Confidence
92% confidence
Finding
Assigning values from ~/.hermes/.env into os.environ makes the imported secrets globally available to the running process, not just this module. That broadens exposure considerably because any code in-process can now read credentials that were silently loaded from disk.

Credential Access

High
Category
Privilege Escalation
Content
- "auth_json" — 从 ~/.hermes/auth.json 的 credential_pool 读
                       条目的 source 可能是 "env:XXX",这种情况下
                       走和上面 "env:XXX" 一样的逻辑(依赖 _load_hermes_env
                       把 ~/.hermes/.env 注入好了),或者直接读 access_token
    """
    # 1. 测试/调试:环境变量覆盖
    env_key = os.environ.get("KIDS_POINTS_LLM_KEY")
Confidence
90% confidence
Finding
The documented behavior explicitly supports resolving keys through auth.json entries that point into ~/.hermes/.env or direct access_token fields, confirming the skill is designed to consume user-global secret stores. For a family-oriented points skill, this is an unnecessary and risky privilege expansion.

Credential Access

High
Category
Privilege Escalation
Content
capture_output=True,
            text=True,
            timeout=30,
            env={**os.environ},  # 继承 ~/.hermes/.env (LLM API key) 等
        )
    except subprocess.TimeoutExpired:
        return "⚠️ V2 处理超时 (30s), 请重试"
Confidence
88% confidence
Finding
The comment confirms that secrets such as LLM API keys are intentionally inherited by the subprocess. In combination with the environment-controlled runtime directory and execution of local Python code, this creates a credible credential-exposure path: substituted or compromised child code can read and exfiltrate those secrets.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
agent-handler.js:53