Back to skill

Security audit

可接入飞书的senseaudio角色扮演助手

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its voice-companion purpose, but it needs review because it handles private chat and audio data through external services while using broad triggers, runtime installs, configurable endpoints, and weak local state controls.

Review before installing in any shared or sensitive Feishu workspace. Require user consent for private chat/audio processing, restrict the bot to intended chats, pin and install dependencies only during setup, allowlist SenseAudio HTTPS endpoints, store persona per user or chat, and delete generated audio files after sending unless retention is explicitly needed.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T02 · Agent Memory Poisoning

Error
Location
scripts/main.py:31
Finding

Persistent Persona Data Can Inject Instructions into Future Agent Prompts

Content
View full analysis
None: persona = load_persona() persona.update( { "name": args.name, "relationship": args.relationship, "personality": args.personality, "speaking_style": args.speaking_style, "catchphrase": args.catchphrase, "voice_id": args.voice_id, "speed": float(args.speed), "pitch": int(round(float(args.pitch))), "vol": float(args.vol), "emotion": args.emotion, "boundaries": args.boundaries, } ) path = save_persona(persona) print(f"人设已保存到: {path}") print(json.dumps(persona, ensure_ascii=False, indent=2)) ``` ```python def cmd_persona_prompt(args: argparse.Namespace) -> None: persona = load_persona() user_message = args.user_message.strip() prompt = f""" 你现在扮演一个固定虚拟角色,请严格保持人设一致。 角色名称:{persona['name']} 角色定位:{persona['relationship']} 性格关键词:{persona['personality']} 说话风格:{persona['speaking_style']} 常用口头禅:{persona.get('catchphrase', '')} 角色边界:{persona.get('boundaries', '')} 要求: 1. 使用自然中文回复,不要变成客服口吻。 2. 语气要符合这个角色,不要跑出设定。 3. 回复适合被 TTS 念出来,默认 2~5 句,不要过长。 4. 对危险、违法、医疗、财务等高风险内容要温和收缩,不要直接给专业决策。 5. 可以表现亲近感,但不要越界。 用户刚刚说:{user_message} 现在请直接生成这个角色会说的话,只输出回复正文,不要输出解释。 """.strip() print(prompt) ``` ```python def save_persona(persona: Dict[str, Any]) -> Path: PERSONA_PATH.write_text(json.dumps(persona, ensure_ascii=False, indent=2), encoding="utf-8") return PERSONA_PATH ``` ### Technical Analysis Persona fields supplied through command-line arguments are written to persistent storage without authorization checks, validation, length limits, or filtering. These values are subsequentl ...[truncated 1723 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/asr.py:40
Finding

Configurable Endpoints Can Receive SenseAudio Credentials and Private Conversation Content

Content
View full analysis
Dict[str, Any]: requests = _requests_module() api_key = os.getenv("SENSEAUDIO_API_KEY") if not api_key: raise RuntimeError("缺少环境变量 SENSEAUDIO_API_KEY") endpoint = os.getenv("SENSEAUDIO_ASR_ENDPOINT", "https://api.senseaudio.cn/v1/asr") path = Path(audio_path).expanduser().resolve() if not path.exists(): raise FileNotFoundError(f"找不到音频文件: {path}") headers = {"Authorization": f"Bearer {api_key}"} with path.open("rb") as f: files = {"file": (path.name, f, "application/octet-stream")} data = {"model": os.getenv("SENSEAUDIO_ASR_MODEL", "SenseAudio-ASR")} resp = requests.post(endpoint, headers=headers, files=files, data=data, timeout=120) ``` ```python api_key = os.getenv("SENSEAUDIO_API_KEY") if not api_key: raise RuntimeError("缺少环境变量 SENSEAUDIO_API_KEY") base = os.getenv("SENSEAUDIO_API_BASE", "https://api.senseaudio.cn").rstrip("/") endpoint = f"{base}/v1/t2a_v2" OUTPUT_DIR.mkdir(parents=True, exist_ok=True) if output_wav: wav_path = Path(output_wav).expanduser().resolve() wav_path.parent.mkdir(parents=True, exist_ok=True) else: wav_path = OUTPUT_DIR / "reply.wav" payload = { "model": os.getenv("SENSEAUDIO_TTS_MODEL", "SenseAudio-TTS-1.0"), "text": text, "stream": False, "voice_setting": { "voice_id": voice_id, "speed": float(speed), "pitch": int(round(float(pitch))), "vol": float(vol), }, "audio_setting": { "format": "wav", "sample_rate": int(os.getenv("SENSEAUDIO_TTS_SAMPLE_RATE", "24000")), "channel": 1, }, } if emotion: payload["voice_setting"]["emotion"] = emotion headers = { "Authorization": f"Bearer {api_key ...[truncated 2085 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/feishu_api.py:13
Finding

Runtime Installation of an Unpinned Dependency Exposes the Host to Supply-Chain Code Execution

Content
View full analysis
None: pip_name = pip_name or import_name try: importlib.import_module(import_name) return except ImportError: pass result = subprocess.run( [sys.executable, "-m", "pip", "install", pip_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) if result.returncode != 0: raise RuntimeError( f"缺少 Python 依赖 {pip_name},自动安装失败。\n" f"请手动执行: {sys.executable} -m pip install {pip_name}\n" f"stderr:\n{result.stderr}" ) ``` ### Technical Analysis When `requests` is unavailable, the Skill invokes pip automatically during normal feature execution. The requested dependency has no pinned version, lockfile, hash verification, or trusted-index enforcement. Python package installation can execute package build and installation code with the privileges of the Skill process. Therefore, compromise of the configured package index, dependency resolution path, or a future package release could turn an ordinary ASR, TTS, or Feishu operation into local arbitrary code execution. The static package name is `requests`, so direct command injection through `pip_name` is not exposed by the current call sites. The confirmed risk is the unreviewed and mutable supply-chain payload introduced at runtime. ### Attack Path 1. The deployment does not already contain the `requests` module, or an attacker causes its import to fail. 2. The attacker influences pip configuration, DNS/network routing, a configured package mirror, or package resolution. 3. A ...[truncated 757 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/main.py:96
Finding

Synthesized Conversation Audio Is Stored in Predictable Files Without Cleanup

Content
View full analysis
None: persona = load_persona() chat_id = args.chat_id or os.getenv("FEISHU_CHAT_ID") if not chat_id: raise RuntimeError("缺少 chat_id,请通过 --chat-id 传入,或设置 FEISHU_CHAT_ID") reply_text = args.reply_text.strip() if not reply_text: raise RuntimeError("reply_text 不能为空") base_name = args.file_name or f"{persona['name']}_reply" wav_path = OUTPUT_DIR / f"{base_name}.wav" opus_path = OUTPUT_DIR / f"{base_name}.opus" tts_result = synthesize( text=reply_text, voice_id=args.voice_id or persona["voice_id"], speed=args.speed if args.speed is not None else persona["speed"], pitch=args.pitch if args.pitch is not None else persona["pitch"], vol=args.vol if args.vol is not None else persona["vol"], emotion=args.emotion or persona.get("emotion"), output_wav=str(wav_path), ) convert_wav_to_opus(tts_result["wav_path"], str(opus_path)) upload = upload_opus_file(str(opus_path), file_name=f"{base_name}.opus") ``` ### Technical Analysis Each reply is stored as both WAV and OPUS under a predictable project-local `outputs` directory. Neither file is deleted after conversion or successful upload. No restrictive file mode is explicitly established, and the default file name is derived from persistent persona data. The optional `--file-name` value is also accepted without sanitization. This creates unnecessary long-term retention of conversational material. Predictable shared paths can additionally cause concurrent operations to overwrite on ...[truncated 1092 chars]
Remediation
View remediation
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (46)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的核心能力是一个完整的飞书语音陪伴机器人,包括消息触发、角色扮演对话生成、首次人设配置,以及通过 TTS 生成语音回复。而提供的代码片段只负责将音频文件上传到 SenseAudio ASR 服务做语音识别,返回文本结果。它没有看到任何飞书集成、对话逻辑、角色设定、文本生成或语音合成功能。虽然语音识别可能是处理用户语音输入的一个支持性组件,但从该代码片段本身看,其实际行为与声明的主要用途存在明显差异,且实现方向更接近“ASR 转写工具”而不是“语音陪伴技能”。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

该描述把技能表述为一个完整的飞书语音陪伴机器人能力,但代码片段只是底层/辅助式 CLI 工具集合。它确实涉及人设管理、ASR 转写、TTS 合成和飞书语音发送,这些与声明部分相关;但核心能力缺失:没有监听飞书私聊消息、没有基于文字/语音输入自动处理、没有真正调用大模型生成回复,只是生成一个可供外部模型使用的 prompt 文本。此外,“首次使用引导设置人设”也未实现为聊天流程,仅有手动 persona-init 命令。因此代码行为与声明的主要用途存在实质性不一致。

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill is intended to trigger on essentially all private chat text and voice messages, creating continuous collection and processing of highly sensitive conversational data. In the context of an emotional companion bot, users are likely to share intimate or confidential content, so a broad trigger surface materially increases privacy, consent, and inadvertent data-handling risk.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 46)May include surrounding context.

md
OpenClaw 的 Skills 通过 `SKILL.md` frontmatter 声明依赖与环境变量;飞书渠道使用 WebSocket 事件订阅接收消息;飞书发送语音消息前需要先上传 OPUS 文件,再以 `audio` 消息发送 `file_key`。

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation states that user voice is transcribed by SenseAudio ASR and replies are synthesized by SenseAudio TTS, but it does not warn that user content is transmitted to third-party services. In a companion/emotional chat context, messages and voice recordings may contain highly sensitive personal data, so lack of disclosure and consent handling creates meaningful privacy and compliance risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.md (reported line 35)May include surrounding context.

export FEISHU_CHAT_ID="oc_xxx" # 可选,也可通过命令传 --chat-id export SENSEAUDIO_API_KEY="your_key" export SENSEAUDIO_API_BASE="https://api.senseaudio.cn" export SENSEAUDIO_ASR_ENDPOINT="https://api.senseaudio.cn/v1/asr" # 如果你们实际路径不同,请替换

text

## 飞书配置

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The README instructs operators to also send the generated text reply to the user, which conflicts with the skill's declared voice-only behavior. This can cause unintended disclosure of sensitive or intimate conversation content in persistent text form, increasing privacy and retention risks beyond what users may expect from a voice-only companion skill.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill declares capabilities that imply access to environment secrets, filesystem, network, and shell execution, but it does not define any explicit tool scope or permission boundaries. In a skill that processes arbitrary user messages and may invoke scripts, this increases the risk of over-privileged execution, secret exposure, or unintended command/file operations if the runtime grants broad defaults.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation says user text and voice will be processed via SenseAudio, but it does not prominently warn users that their private messages and audio may be uploaded to a third-party service. Because this skill targets ongoing private chats and voice messages, the missing disclosure undermines informed consent and can expose sensitive personal data to external processors without clear notice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
71% confidence
Finding

技能描述与交互要求全部以中文给出,且未说明是否支持其他语言或允许用户自行选择语言/locale。对于面向通用聊天的技能,这种隐含的单一语言约束可能构成语言/locale 策略问题。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The persona fields are written entirely in Chinese and define the assistant's name, relationship, personality, and catchphrase in a way that strongly implies a fixed Chinese-language interaction style. There is no indication that the user can choose another language or that this locale restriction is justified as a region-specific skill.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Automatically installing Python packages at runtime is unnecessary for normal ASR helper behavior and introduces supply-chain risk. In a chat skill context, this means code execution behavior can change at runtime based on missing dependencies, making the environment less predictable and harder to audit securely.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The code executes a pip install at runtime via subprocess when an import is missing. Even though it does not invoke a shell, it still allows code and dependencies to be fetched and installed dynamically during skill execution, which expands the attack surface and can lead to unreviewed package installation or dependency confusion if package names or indexes are influenced by the environment.

Content

Scanner excerpt · scripts/asr.py (reported line 20)May include surrounding context.

python
except ImportError:
        pass

    result = subprocess.run(
        [sys.executable, "-m", "pip", "install", pip_name],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Automatically installing a package without prior notice is risky because it changes the runtime and may pull executable code from external sources unexpectedly. In managed skill environments this can bypass normal review and introduce both supply-chain and operational risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The runtime error strings are written in Chinese and the code does not offer any locale selection or user opt-in. This creates a language policy concern because the skill imposes a specific language on users regardless of their preferred locale.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The manifest emphasizes generating persona-consistent text and synthesizing voice replies, and specifically says not to send text messages back to the chat. This file implements outbound upload of user audio to an ASR service and returns extracted text, which is a distinct speech-recognition behavior not explicitly described in the manifest.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The code sends user audio to an external service endpoint over the network. External transmission is expected for cloud ASR, but in this skill's intimate companion-chat context it is more sensitive because private voice content is being shared with a third party and could expose personal data if users are not informed or if the endpoint is overridden unsafely.

Content

Scanner excerpt · scripts/asr.py (reported line 46)May include surrounding context.

python
if not api_key:
        raise RuntimeError("缺少环境变量 SENSEAUDIO_API_KEY")

    endpoint = os.getenv("SENSEAUDIO_ASR_ENDPOINT", "https://api.senseaudio.cn/v1/asr")
    path = Path(audio_path).expanduser().resolve()
    if not path.exists():
        raise FileNotFoundError(f"找不到音频文件: {path}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The function uploads the full audio file to a remote ASR endpoint without any visible consent, disclosure, or minimization controls. In a virtual companion/private chat setting, voice messages may contain highly sensitive personal or emotional content, so undisclosed third-party transmission creates meaningful privacy and compliance risk.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

A Feishu voice-reply skill does not need to modify its Python environment during execution, so invoking pip is unnecessary and risky. Installing packages at runtime can execute attacker-controlled or compromised package code and bypass normal dependency review and deployment controls.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The code executes pip at runtime via subprocess to install whatever package name is passed into ensure_python_package. Even though the current call site uses a fixed package name, runtime installation expands the attack surface to package supply-chain compromise, unreviewed code execution during install, and unpredictable behavior in production environments.

Content

Scanner excerpt · scripts/feishu_api.py (reported line 21)May include surrounding context.

python
except ImportError:
        pass

    result = subprocess.run(
        [sys.executable, "-m", "pip", "install", pip_name],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill silently installs a package without user or operator confirmation, changing the host environment as a side effect of normal execution. This is dangerous because it hides privileged behavior, makes incident review harder, and can pull untrusted code from package repositories without approval.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The function reads FEISHU_APP_ID and FEISHU_APP_SECRET from environment variables and sends them in an HTTP request to obtain an access token. The code contains no user-facing warning, logging, or explanatory comment indicating that credentials will be accessed and transmitted to the Feishu API.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/feishu_api.py (reported line 48)May include surrounding context.

python
if not app_id or not app_secret:
        raise RuntimeError("缺少 FEISHU_APP_ID 或 FEISHU_APP_SECRET")

    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=30,

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/feishu_api.py (reported line 74)May include surrounding context.

python
if not app_id or not app_secret:
        raise RuntimeError("缺少 FEISHU_APP_ID 或 FEISHU_APP_SECRET")

    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=30,

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/feishu_api.py (reported line 100)May include surrounding context.

python
if not app_id or not app_secret:
        raise RuntimeError("缺少 FEISHU_APP_ID 或 FEISHU_APP_SECRET")

    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=30,

Static analysis

No suspicious patterns detected.