Back to skill

Security audit

memory-assistant

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent memory and reminder assistant, but it should be reviewed because it stores personal reminders and item locations, sends spoken text to SenseAudio, and includes file-writing scripts with containment risks.

Install only if you are comfortable storing reminders and item locations locally and sending spoken reminder/location text to SenseAudio. Avoid putting highly sensitive locations, travel details, secrets, or medical/financial reminders into it unless you accept that cloud TTS exposure, and do not run the daemon or cron setup until you have reviewed the storage path and file-writing behavior.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/run_reminders.py:139
Finding
Path Traversal Through an Unvalidated Reminder Identifier## Vulnerability Details **File Location**: `scripts/run_reminders.py`, lines 72–78 and 135–147 **Vulnerability Type**: Path traversal leading to an out-of-scope file write **Risk Level**: Medium ### Vulnerable Code ```python def tts_and_save(text: str, out_path: Path, api_key: str, voice_id: str = DEFAULT_VOICE) -> Path: headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} body = { "model": "SenseAudio-TTS-1.0", "text": text, "stream": False, "voice_setting": {"voice_id": voice_id}, } r = requests.post(TTS_URL, headers=headers, json=body, timeout=30) r.raise_for_status() data = r.json() if data.get("base_resp", {}).get("status_code") != 0: raise RuntimeError(data.get("base_resp", {}).get("status_msg", "TTS failed")) hex_audio = data.get("data", {}).get("audio") if not hex_audio: raise RuntimeError("No audio in response") out_path.parent.mkdir(parents=True, exist_ok=True) with open(out_path, "wb") as f: f.write(bytes.fromhex(hex_audio)) return out_path ``` ```python for r in to_notify: event = r.get("event", "提醒") text = f"提醒:{event}" if dry_run: print(f"[dry-run] would speak: {text}", file=sys.stderr) continue out_path = data_dir / "audio" / f"reminder_{r.get('id', '')}.mp3" try: tts_and_save(text, out_path, api_key, voice_id) play_audio(out_path) except Exception as e: print(f"TTS/play failed for reminder {r.get('id')}: {e}", file=sys.stderr) continue r["status"] = "notified" ``` ### Technical Analysis The reminder identifier is loaded from `reminders.json` and interpolated directly into an output path without validating its characters or checking the resolved destination. Path separators and parent-directory components in the identifier are therefore interpret ...[truncated 1937 chars]
Remediation
## Remediation Suggestions 1. Generate reminder identifiers internally using UUIDs rather than accepting storage-derived identifiers as filenames. 2. Enforce a strict allowlist before using an identifier, such as `[A-Za-z0-9_-]+`, with a reasonable maximum length. 3. Construct and resolve the output path, then verify that it remains beneath the resolved audio directory: ```python import re from uuid import UUID reminder_id = str(r.get("id", "")) if not re.fullmatch(r"[A-Za-z0-9_-]{1,128}", reminder_id): raise ValueError("Invalid reminder ID") audio_dir = (data_dir / "audio").resolve() audio_dir.mkdir(parents=True, exist_ok=True) out_path = (audio_dir / f"reminder_{reminder_id}.mp3").resolve() if out_path.parent != audio_dir: raise ValueError("Output path escapes audio directory") ``` 4. Where supported, use no-follow and exclusive file-opening controls to reduce symlink and overwrite risks. 5. Validate the complete reminder schema when records are created and again when they are loaded from disk. 6. Write reminder data and audio files using restrictive permissions appropriate for potentially sensitive reminder content.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/speak.py:139
Finding
Predictable Shared Temporary File Enables Symlink-Based File Overwrite## Vulnerability Details **File Location**: `scripts/speak.py`, lines 72–76 and 137–145 **Vulnerability Type**: Unsafe predictable temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```python hex_audio = data.get("data", {}).get("audio") if not hex_audio: raise RuntimeError("No audio in response") out_path.parent.mkdir(parents=True, exist_ok=True) with open(out_path, "wb") as f: f.write(bytes.fromhex(hex_audio)) return out_path ``` ```python api_key = get_api_key() out_path = args.out or Path(tempfile.gettempdir()) / "memory_assistant_tts.mp3" tts_senseaudio(text, out_path, api_key, args.voice) print(out_path.resolve(), file=sys.stderr if args.play else sys.stdout) if args.play: play_audio(out_path) ``` ### Technical Analysis When the user does not provide `--out`, the script always writes to the same predictable name in the system temporary directory: ```text <temporary-directory>/memory_assistant_tts.mp3 ``` System temporary directories are commonly shared between local users. The script opens this path using ordinary Python file semantics with truncation enabled and without exclusive creation or a no-follow option. If the path already exists as a symbolic link, the write may follow that link. Platform-level temporary-directory protections can mitigate some cross-user symlink attacks, but they are not universally available or consistently configured. The fixed name also causes concurrent Skill invocations to overwrite one another's output. ### Attack Path 1. A local attacker predicts the fixed temporary filename. 2. Before the victim runs the script, the attacker creates that path as a symbolic link to a file writable by the victim account. 3. The victim executes `speak.py` without specifying `--out`. 4. SenseAudio returns synthesized audio. 5. The script opens the predictable path with `"wb"`, potentially follows the symbolic link, and truncates th ...[truncated 731 chars]
Remediation
## Remediation Suggestions 1. Replace the fixed filename with a securely and atomically created unique temporary file: ```python if args.out is None: fd, generated_path = tempfile.mkstemp( prefix="memory_assistant_", suffix=".mp3", ) os.close(fd) out_path = Path(generated_path) else: out_path = args.out ``` 2. Prefer `NamedTemporaryFile(delete=False, suffix=".mp3")` when compatible with the playback behavior of the target operating system. 3. Create temporary files with restrictive permissions, such as owner read/write only. 4. Where available, use `O_NOFOLLOW` to reject symbolic links and `O_EXCL` for new files. 5. If user-provided `--out` paths are intentionally supported, document that they may overwrite existing files and consider requiring explicit confirmation or an overwrite flag. 6. Delete automatically generated temporary audio after playback when retention is unnecessary.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (25)

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill sends reminder text and item-location content to an external TTS provider, but the description does not clearly warn users that this information leaves the local environment. Since the data may include sensitive personal schedules, meeting details, and locations of valuables such as keys or passports, this omission creates a meaningful privacy and data-handling risk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill describes capabilities that require environment access, file reads/writes, network calls, and shell execution, but it declares no explicit tool scope or permission boundaries. This increases the risk that the runtime grants broader access than users or reviewers expect, especially because the skill handles persistent storage and external API calls.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases include very common expressions like “记一下” and “提醒我,” which can appear in ordinary conversation and cause the skill to activate unintentionally. Because the skill stores user content and may later send reminder text or location data to an external TTS service, accidental invocation can lead to unintended persistence or disclosure of sensitive information.

External Transmission

Medium
Category
Data Exfiltration
Content
所有需要“读出来”的提醒(如:到点提醒、查询到的位置)均通过 SenseAudio 文本转语音 API 合成。

- **接口**:`POST https://api.senseaudio.cn/v1/t2a_v2`
- **鉴权**:`Authorization: Bearer YOUR_API_KEY`
- **必填参数**:`model: "SenseAudio-TTS-1.0"`,`text`(要朗读的文案),`voice_setting.voice_id`(如 `male_0004_a`)
- **流式**:可设 `stream: false` 一次取回整段音频;响应中 `data.audio` 为 hex 编码,需解码为二进制后保存为 mp3/wav 或送入播放器。
Confidence
89% confidence
Finding
This is a real external data transmission path: the skill posts user-provided text to the SenseAudio API for voice generation. In context, the transmitted text can contain sensitive reminders, schedules, and the storage locations of important personal items, so the network behavior is security-relevant and not merely incidental.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The natural-language description and all listed triggers are exclusively in Chinese, which implies a fixed language requirement with no documented user choice or opt-in. This can violate language/locale policy when the skill does not clearly offer multilingual support or explain a justified locale limitation.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger list contains common conversational phrases and everyday nouns such as '提醒我', '放哪儿/放在哪', and item names like '护照' and '备用钥匙'. This can cause the skill to activate unintentionally during ordinary conversation, leading to unsolicited capture of sensitive location/reminder data or unexpected voice reminder behavior.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The entire skill example and invocation flow are written as Chinese-only interactions, including fixed trigger phrasing like "记一下" and responses such as "嘿 Gemini" examples, with no indication that users may choose another language. Under the policy, a skill that imposes a specific language without user opt-in is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The documentation instructs sending reminder text to a third-party TTS API and using an API key, but it does not explicitly warn that user-provided reminder contents may leave the local device. Because reminder text can contain sensitive personal schedule or location information, this creates a privacy and data-handling risk if users are unaware of the external transfer.

External Transmission

Medium
Category
Data Exfiltration
Content
更多见:<https://senseaudio.cn/docs/voice_api>。

### cURL 示例(同步,保存为 mp3)

```bash
# 1. 请求并保存 JSON
Confidence
89% confidence
Finding
The cURL example demonstrates sending reminder text and authorization credentials to an external TTS endpoint. As documented behavior for a memory/reminder assistant, this can expose private schedule content to a third party if users or implementers follow the example without understanding the privacy implications.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# 1. 请求并保存 JSON
curl -X POST https://api.senseaudio.cn/v1/t2a_v2 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
Confidence
88% confidence
Finding
This example shows a concrete external transmission of text payloads and an authorization bearer token to SenseAudio. In a skill designed to store memories and reminders, the transmitted content can easily contain personally sensitive data, making the network call materially security-relevant.

External Transmission

Medium
Category
Data Exfiltration
Content
"stream": False,
        "voice_setting": {"voice_id": "male_0004_a"}
    }
    r = requests.post(url, headers=headers, json=body)
    r.raise_for_status()
    data = r.json()
    if data.get("base_resp", {}).get("status_code") != 0:
Confidence
92% confidence
Finding
The example code performs an outbound POST request that transmits arbitrary reminder text to an external service. In this skill's context, the text may encode sensitive personal reminders, meeting details, or item-location information, so the transmission is security-relevant even though it appears to be intended functionality.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Error: pip install requests", file=sys.stderr)
    sys.exit(1)

TTS_URL = "https://api.senseaudio.cn/v1/t2a_v2"
DEFAULT_VOICE = "male_0004_a"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Error: pip install requests", file=sys.stderr)
    sys.exit(1)

TTS_URL = "https://api.senseaudio.cn/v1/t2a_v2"
DEFAULT_VOICE = "male_0004_a"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Error: pip install requests", file=sys.stderr)
    sys.exit(1)

TTS_URL = "https://api.senseaudio.cn/v1/t2a_v2"
DEFAULT_VOICE = "male_0004_a"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print("Error: pip install requests", file=sys.stderr)
    sys.exit(1)

TTS_URL = "https://api.senseaudio.cn/v1/t2a_v2"
DEFAULT_VOICE = "male_0004_a"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
"stream": False,
        "voice_setting": {"voice_id": voice_id},
    }
    r = requests.post(TTS_URL, headers=headers, json=body, timeout=30)
    r.raise_for_status()
    data = r.json()
    if data.get("base_resp", {}).get("status_code") != 0:
Confidence
90% confidence
Finding
The script sends reminder text to an external TTS provider, which can expose potentially sensitive personal data such as item locations, schedules, or other spoken reminder contents. In the context of a memory/reminder assistant, that data is especially privacy-sensitive because reminders may contain secrets about a user's whereabouts, possessions, or routine.

Tainted flow: 'out_path' from requests.post (line 144, network input) → open (file write)

Medium
Category
Data Flow
Content
if not hex_audio:
        raise RuntimeError("No audio in response")
    out_path.parent.mkdir(parents=True, exist_ok=True)
    with open(out_path, "wb") as f:
        f.write(bytes.fromhex(hex_audio))
    return out_path
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

External Transmission

Medium
Category
Data Exfiltration
Content
"stream": False,
        "voice_setting": {"voice_id": voice_id},
    }
    r = requests.post(TTS_URL, headers=headers, json=body, timeout=30)
    r.raise_for_status()
    data = r.json()
    if data.get("base_resp", {}).get("status_code") != 0:
Confidence
96% confidence
Finding
This network call transmits user-provided text or derived item-location phrases to an external service endpoint. Given the skill’s purpose—remembering object locations and reminders—the data can reveal sensitive behavioral patterns, schedules, and where important items are kept, so external transmission meaningfully increases privacy exposure.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script sends arbitrary reminder text and item-location content to a third-party TTS API, and this content can contain sensitive personal information such as where valuables are stored or private schedule details. In the context of a memory/reminder assistant, that makes the privacy risk more significant because the transmitted data is inherently personal and the network transfer happens without an explicit consent or warning mechanism at use time.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
path = str(filepath.resolve())
    system = platform.system()
    if system == "Darwin":
        subprocess.run(["afplay", path], check=True)
    elif system == "Windows":
        os.startfile(path)
    elif system == "Linux":
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
path = str(filepath.resolve())
    system = platform.system()
    if system == "Darwin":
        subprocess.run(["afplay", path], check=True)
    elif system == "Windows":
        os.startfile(path)
    elif system == "Linux":
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
elif system == "Linux":
        for cmd in (["paplay", path], ["aplay", path], ["ffplay", "-nodisp", "-autoexit", path]):
            try:
                subprocess.run(cmd, check=True, capture_output=True)
                return
            except (FileNotFoundError, subprocess.CalledProcessError):
                continue
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
elif system == "Linux":
        for cmd in (["paplay", path], ["aplay", path], ["ffplay", "-nodisp", "-autoexit", path]):
            try:
                subprocess.run(cmd, check=True, capture_output=True)
                return
            except (FileNotFoundError, subprocess.CalledProcessError):
                continue
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
The file consistently presents the skill instructions and usage in Chinese, and there is no indication that the user can choose another language or that the locale restriction is intentional and justified. This can violate language/locale policy when a specific language is effectively forced without opt-in.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The natural-language description, usage instructions, option descriptions, and user-facing argparse/help text are written in Chinese only. That forces a specific language experience without indicating user choice, which matches the language/locale policy concern for natural-language content.

Static analysis

No suspicious patterns detected.