Back to skill

Security audit

ai-soulmate

Security checks for vulnerabilities and agentic risk

Overview

The skill is not malicious, but it needs review because it stores sensitive psychological chat data by default and ships a local web server with security weaknesses.

Install only after reviewing the logging and server behavior. For sensitive or minor-facing use, set SPL_LOG=0 and SPL_AUDIT_LOG=0 unless retention is explicitly needed, keep SPL_BIND on 127.0.0.1, avoid custom LLM endpoints unless trusted, and do not rely on the local HTTP UI for exposed or multi-user deployments without fixing XSS, authentication, and request limits.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Warning
Location
assets/minor-protection/SPL-anthropic-minor-server.py:403
Finding
DOM-based cross-site scripting in the local chat interface<![CDATA[ ## Vulnerability Details **File Location**: `assets/minor-protection/SPL-anthropic-minor-server.py`, lines 403–408 **Vulnerability Type**: DOM-based cross-site scripting through unsafe HTML rendering **Risk Level**: Medium ### Vulnerable Code ```javascript function add(role, text, status, crisis){ var box=document.getElementById('chat'); var d=document.createElement('div'); d.className='msg '+(crisis?('agent crisis'):role); var s=''; if(status){s='<span class="status">'+status.map(function(t){return '<span class="tag'+(t.hot?' hot':'')+'">'+t.name+' '+t.val+'</span>'}).join('')+'</span>';} d.innerHTML=text+s+'<span class="meta">'+(role==='user'?'you':'SPL partner')+'</span>'; box.appendChild(d); box.scrollTop=box.scrollHeight; } ``` The displayed labels in the original source are localized, but the vulnerable operation is the assignment of attacker-controlled `text` to `d.innerHTML`. ### Technical Analysis The `text` argument is populated directly from chat input and is rendered using `innerHTML` without encoding or sanitization. Consequently, the browser interprets HTML supplied by a user instead of treating it as plain conversation text. The same rendering function displays both user messages and server replies. User input reaches it directly through `add('user', t)`, so exploitation does not depend on the server reflecting the input. Because the page does not deploy a restrictive Content Security Policy, injected elements with executable event handlers can run JavaScript in the chat application's origin. ### Attack Path 1. An attacker prepares a message containing an HTML element with an executable event handler. 2. The attacker convinces a user to submit or paste that message into the chat interface. 3. The `send()` function invokes `add('user', t)` before sending the request. 4. `add()` assigns the message to `d.innerHTML`. 5. The browser creates the attacker-controlled element and executes its handler. 6. The injected script ca ...[truncated 683 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `innerHTML` with `textContent` for all conversation text. - Construct status and metadata elements through `document.createElement()` and assign their values through `textContent`. - If limited formatting is required, pass content through a well-maintained sanitizer with a strict element and attribute allowlist. - Add a restrictive Content Security Policy that disallows inline script and inline event handlers. - Add regression tests using messages containing tags, entity encodings, SVG elements, and event-handler attributes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
assets/minor-protection/SPL-anthropic-minor-server.py:178
Finding
Unauthenticated and unbounded HTTP requests can exhaust server resources<![CDATA[ ## Vulnerability Details **File Location**: `assets/minor-protection/SPL-anthropic-minor-server.py`, lines 178–194 and 457–466 **Vulnerability Type**: Unbounded request processing and session allocation **Risk Level**: Medium ### Vulnerable Code ```python SESSIONS = {} def _new_session_id(): return "uid_" + "".join(random.choices(string.ascii_lowercase + string.digits, k=12)) def get_session(session_id): with _lock: if session_id not in SESSIONS: SESSIONS[session_id] = SPLMinorPureCore( minor_mode=True, audit_log_dir="logs", audit_session_id=session_id, ) return SESSIONS[session_id] ``` ```python def do_POST(self): if self.path == "/api/chat": try: length = int(self.headers.get("Content-Length", 0)) raw = self.rfile.read(length) if length else b"" data = json.loads(raw.decode("utf-8") or "{}") text = (data.get("text") or "").strip() session_id = (data.get("session_id") or "").strip() if not text: return self._json({"error": "empty"}, 400) return self._json(handle_chat(text, session_id)) ``` ### Technical Analysis The server accepts a caller-controlled `Content-Length` and reads that amount without enforcing a maximum body size. It also accepts arbitrary session identifiers and creates a new `SPLMinorPureCore` object for every previously unseen value. Created sessions remain indefinitely in the global `SESSIONS` dictionary. There is no session expiration, eviction, maximum session count, authentication, per-client rate limit, or validation that a session identifier was generated by the server. Each new session can also produce associated audit and request log files. This creates multiple resource-exhaustion dimensions: - Memory growth from persistent engine instances. - Disk and inode consumption from per-session logs. - CPU consumption ...[truncated 1422 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject requests whose `Content-Length` exceeds a small documented limit before reading the body. - Apply socket read timeouts and concurrency limits. - Generate session IDs exclusively on the server and validate their syntax and length. - Do not create a new session merely because a caller submits an unknown identifier. - Add a bounded session store with inactivity expiration and least-recently-used eviction. - Limit the number of sessions and requests per client. - Disable or aggregate per-session logs to prevent inode exhaustion. - Require authentication and anti-CSRF protections for any deployment beyond loopback. - Refuse non-loopback binding unless an explicit secure-deployment option confirms that authentication, TLS termination, and rate limiting are present. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
assets/minor-protection/SPL-anthropic-minor-server.py:201
Finding
Sensitive conversation and psychological-state data is logged in plaintext by default<![CDATA[ ## Vulnerability Details **File Location**: `assets/minor-protection/SPL-anthropic-minor-server.py`, lines 201–238; related audit logging in `scripts/SPL-anthropic-engine.py`, lines 22–60 **Vulnerability Type**: Plaintext retention of sensitive user-derived data **Risk Level**: Medium ### Vulnerable Code ```python REQUEST_LOG_DIR = os.environ.get("SPL_LOG_DIR", "logs") REQUEST_LOG_ENABLED = os.environ.get("SPL_LOG", "1") != "0" _REQUEST_LOG_MAX_INPUT = 120 _request_log_lock = threading.Lock() def _mask(text): if text is None: return None text = " ".join(str(text).split()) if len(text) > _REQUEST_LOG_MAX_INPUT: return text[:_REQUEST_LOG_MAX_INPUT] + "…[truncated]" return text def log_request(session_id, user_text, intent, crisis_cat, result): if not REQUEST_LOG_ENABLED: return try: os.makedirs(REQUEST_LOG_DIR, exist_ok=True) path = os.path.join(REQUEST_LOG_DIR, f"request-{session_id}.jsonl") entry = { "ts": datetime.datetime.now().isoformat(timespec="milliseconds"), "session": session_id, "user_input_masked": _mask(user_text), "intent": intent, "crisis_category": crisis_cat, "crisis_triggered": crisis_cat is not None, "reply": _mask(result.get("reply")), "guardian_notified": result.get("guardian_notified", False), "risk_level": result.get("state", {}).get("risk_level"), "event": result.get("event"), } with _request_log_lock: with open(path, "a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n") except Exception: pass ``` The core audit logger is also enabled by default through: ```python if enabled is None: enabled = os.environ.get("SPL_AUDIT_LOG", "1") != "0" ``` It records input vectors and state summaries including trauma, mood, self-esteem, and other inferred psycho ...[truncated 1810 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Disable content-derived request logging by default and require explicit operator consent to enable it. - Record non-content operational metrics instead of message excerpts whenever possible. - Do not log crisis text, replies, trauma state, or psychological inferences unless strictly required. - Create log files atomically with owner-only permissions such as `0o600`. - Place logs in a private, explicitly configured directory rather than the current working directory. - Implement configurable retention limits and automatic secure deletion. - Encrypt sensitive logs at rest when retention is necessary. - Separate operational logs from psychological audit records and restrict access independently. - Clearly present consent and retention settings to operators before the first request is accepted. ]]>

T08 · Insecure Dependencies

Note
Location
README.md:41
Finding
Documentation recommends installing an unaudited external package<![CDATA[ ## Vulnerability Details **File Location**: `README.md`, lines 41–47 **Vulnerability Type**: External package supply-chain exposure **Risk Level**: Low ### Vulnerable Code ```bash pip install spl-agent-engine==0.1.0 ``` ```python from spl_agent_engine import SPLPureCoreV7_3 core = SPLPureCoreV7_3() core.process_vector({"belonging": 0.5, "threat": -0.1}, 1.0) print(core.snapshot()) ``` ### Technical Analysis The package version is pinned, which prevents an unbounded latest-version installation. However, the package contents, package hashes, build provenance, and transitive dependencies are not included in the audited artifact. Following this instruction causes `pip` to retrieve and install code delivered separately from the reviewed project. Depending on the package format and environment, installation can process package-controlled build metadata or installation logic. The audit cannot establish that package version `0.1.0` is byte-for-byte equivalent to the reviewed source. This is a supply-chain assurance gap rather than evidence that the named package is malicious. ### Attack Path 1. A user follows the PyPI installation instructions in the README. 2. `pip` contacts the configured package index and retrieves package metadata and artifacts. 3. The retrieved artifact or one of its dependencies supplies code not present in this audit. 4. Installation or subsequent import executes the externally delivered code. 5. If the package, account, index, build pipeline, or dependency is compromised, attacker-controlled code runs with the installing user's privileges. ### Impact Assessment A compromised package could obtain all permissions available to the Python installation process, including access to user files, environment variables, network connectivity, and the target Python environment. No compromise of the referenced package was demonstrated. The confirmed issue is that the recommendation crosses the reviewed trust boundary without hashes or v ...[truncated 25 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer installation directly from the reviewed local source. - Publish cryptographic hashes for every approved distribution artifact. - Provide a hash-locked requirements file and document use of `pip --require-hashes`. - Publish signed provenance linking the package artifact to a specific repository commit. - Document and audit all transitive dependencies. - Reproduce the package build in a controlled environment and verify that the resulting artifact matches the published package. - Avoid privileged or system-wide package installation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/SPL-anthropic-engine.py:1370
Finding
Custom LLM endpoint configuration can disclose API credentials and sensitive prompts<![CDATA[ ## Vulnerability Details **File Location**: `scripts/SPL-anthropic-engine.py`, lines 1370–1414 and 1432–1477 **Vulnerability Type**: Unrestricted outbound endpoint receiving credentials and user-derived data **Risk Level**: Medium ### Vulnerable Code ```python def __init__(self, api_key: str, api_base: str = None, model: str = None, system_prompt: str = None, timeout: int = 30): self.api_key = api_key self.api_base = api_base or self.DEFAULTS["api_base"] self.model = model or self.DEFAULTS["model"] self.system_prompt = system_prompt or self.DEFAULTS["system_prompt"] self.timeout = timeout ``` ```python req = urllib.request.Request( f"{self.api_base.rstrip('/')}/chat/completions", data=json.dumps(payload).encode("utf-8"), headers={ "Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=self.timeout) as resp: body = json.loads(resp.read().decode("utf-8")) choice = body.get("choices", [{}])[0] content = choice.get("message", {}).get("content", "") return content.strip() if content else None except (urllib.error.URLError, json.JSONDecodeError, KeyError) as e: return None ``` The Claude adapter implements equivalent behavior using a configurable `api_base` and the `x-api-key` header. ### Technical Analysis The adapters are opt-in and the documented defaults point to official OpenAI and Anthropic HTTPS endpoints. Therefore, the code is not a hidden or automatically activated exfiltration channel. Nevertheless, `api_base` accepts an arbitrary string without validating the URL scheme, hostname, port, or resolved network destination. When `generate()` is called, the adapter sends: - The supplied API credential. - The system prompt. - The generated style prompt. - Optional example dialogue. - Psychological and emotional ...[truncated 1515 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Allowlist official provider origins by default. - Require an explicit unsafe-compatibility option before accepting a custom endpoint. - Permit only HTTPS for non-local development configurations. - Resolve and reject loopback, private, link-local, multicast, and other special-use destinations unless specifically authorized. - Revalidate destinations after redirects and either disable redirects or enforce the same allowlist on every redirect target. - Bind each credential to its intended provider origin and never reuse production credentials with custom endpoints. - Minimize prompt content before transmission and obtain informed user consent. - Document exactly which state fields may be encoded into outbound prompts. - Use network egress controls to limit the process to approved provider hosts. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (50)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill claims 'zero probabilistic black boxes' and reproducibility, yet the finding notes optional OpenAI/Claude adapters that introduce outbound API calls and non-deterministic model behavior. This is dangerous because users may make privacy and auditability decisions based on a false assumption of local-only deterministic processing when prompts can instead leave the device and produce irreproducible outputs.

Ae1

High
Category
analysis-evasion
Content
- `scripts/SPL-anthropic-engine.py` (Core engine + NarrativeMapper + AuditLogger)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The skill claims fully reproducible, zero-black-box behavior, but this file adds optional remote LLM adapters that send prompt material to third-party APIs and return nondeterministic output. That mismatch is dangerous because downstream integrators may rely on privacy, determinism, or auditability guarantees that are silently invalidated when these adapters are enabled.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
This natural-language clause establishes Chinese as the authoritative language for interpretation whenever the bilingual texts differ. Under the stated policy, forcing a specific language without user choice can be a locale/language policy violation, and the document does not offer an opt-in or alternative authoritative-language selection.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises capabilities that include environment-controlled behavior, local file logging, and network-exposed service behavior, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates a transparency and governance gap: hosts or users may invoke a skill that can write logs or expose an HTTP endpoint without an explicit, machine-readable permission boundary.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger scenarios are broad and loosely bounded, which can cause the skill to activate in contexts beyond the operator's expectations. Over-broad activation is a security concern when the skill can log data, run a server, or influence sensitive persona/prompt generation, because it increases the chance of unintended processing of user content.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The file’s natural-language documentation and user-facing descriptions are entirely in Chinese, with no indication that other languages are supported or that the language is intentionally region-specific. Under the policy, forcing a specific language without user opt-in is a locale/language policy issue.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This Python file embeds user-facing prompt instructions, comments, and example outputs entirely in Chinese, and explicitly frames the module as intended for enterprise/government integration. There is no opt-in, locale selection, or documented justification for forcing Chinese output, which can violate language/locale policy requirements.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Ssd 3

Medium
Confidence
96% confidence
Finding
Persistent JSONL audit logs create a durable data-retention channel for natural-language inputs, session metadata, and inferred state. Because the skill simulates minors' emotional/psychological state, those logs may contain highly sensitive information that can be exposed via filesystem access, log shipping, backups, or accidental support bundle collection.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The audit logger persists raw input metadata, session identifiers, and summarized psychological-state snapshots to local JSONL files by default. In a minor-protection context, this creates a sensitive data retention path for inferred mental-state data about minors, increasing exposure through local compromise, backups, misconfigured permissions, or downstream log collection.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The logger is enabled by default and writes user input plus psychological-state summaries without any runtime consent mechanism or user-facing warning. For a system explicitly modeling minors' cognition and emotion, collecting and storing this category of data without explicit notice and opt-in is a serious privacy and compliance weakness.

Ssd 3

Medium
Confidence
94% confidence
Finding
The guardian-callback design explicitly contemplates sending HIGH-risk snapshots to third parties, which semantically encourages disclosure of inferred psychological state outside the core engine. In the context of a minor-focused companion/psychology skill, that disclosure path is especially sensitive and could violate user expectations, privacy law, or organizational data-handling rules if integrated carelessly.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The comments claim minor_mode should effectively always remain enabled, but the dataclass exposes it as a normal mutable constructor field with no enforcement. This means callers can instantiate or mutate the engine into a less-protected mode despite the safety claims, creating a gap between documented protections and actual runtime behavior.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The code comments warn that snapshot output must not be used directly as downstream image-prompt anchor input, yet the API returns the full snapshot without any tagging, filtering, or technical barrier. In this skill ecosystem, that gap matters because other components could consume these fields and transform minor-state data into downstream prompt material despite the stated prohibition.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
When risk escalates to HIGH, the engine forwards a full snapshot to an external guardian callback with no built-in field minimization, policy gate, or consent enforcement. That snapshot includes inferred emotional and risk metadata, so this creates a direct disclosure channel for sensitive psychological information beyond the local simulation boundary.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Natural-language text throughout the file, including UI labels and replies, is fixed to Chinese and the HTML declares zh-CN, with no option for users to choose another language. Under the stated policy, forcing a specific language without opt-in is a locale policy violation unless clearly documented as region-specific and justified.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
assets/minor-protection/SPL-anthropic-minor-server.py:41