Back to skill

Security audit

AudioClaw Skills Voice Reply

Security checks for vulnerabilities and agentic risk

Overview

This voice-reply skill is coherent, but it needs review because it can automatically send audio through Feishu, infer recipients from local logs, use stored credentials, and execute a caller-selected ffmpeg path.

Install only in an AudioClaw environment where direct Feishu audio sending is intended. Prefer explicit chat IDs over log inference, avoid accepting untrusted request JSON, do not expose ffmpeg_exe or cache_dir to users, review stored credential handling, and treat generated voice replies as data sent to SenseAudio and Feishu.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (4)

T01 · Skill Instruction Hijacking

Error
Location
agents/openai.yaml:4
Finding

Agent Behavior and Output Hijacking Through Skill Instructions

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/openclaw_voice_switchboard.py:1237
Finding

Arbitrary Executable Invocation Through Request-Controlled ffmpeg Path

Content
View full analysis
Dict[str, object]: if args.request_file and args.request_json: raise SystemExit("Use either --request-file or --request-json, not both.") request: Dict[str, object] = {} if args.request_file: request = json.loads(Path(args.request_file).read_text(encoding="utf-8")) elif args.request_json: request = json.loads(args.request_json) direct = { "text": args.text, "scene": args.scene, "voice_id": args.voice_id, "voice_family": args.voice_family, "emotion": args.emotion, "speed": args.speed, "pitch": args.pitch, "volume": args.volume, "audio_format": args.format, "sample_rate": args.sample_rate, "delivery_profile": args.delivery_profile, "ffmpeg_exe": args.ffmpeg_exe, "allow_fallback": args.allow_fallback, "strict_voice": args.strict_voice, "cache_dir": args.cache_dir, "validated_only": args.validated_only, "preference_key": args.preference_key, "reply_mode": args.reply_mode, } ``` The executable is selected from request-controlled data: ```python ffmpeg_exe = resolve_ffmpeg_exe( str(request.get("ffmpeg_exe") or args.ffmpeg_exe or "") ) if delivery_profile == "feishu_voice" else "" ``` The selected path is executed directly: ```python def transcode_for_delivery( source_path: Path, target_path: Path, *, profile: str, ffmpeg_exe: str, ) -> None: if profile != "feishu_voice": if source_path != target_path: shutil.copyfile(source_path, target_path) return if target_path.suff ...[truncated 2919 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/openclaw_voice_switchboard.py:1221
Finding

Shared Predictable Temporary Cache Enables Symlink and Cache-Poisoning Attacks

Content
View full analysis
Path: digest = hashlib.sha256( json.dumps( { "text": text, "voice_id": voice_id, "settings": settings, }, ensure_ascii=False, sort_keys=True, ).encode("utf-8") ).hexdigest() extension = settings["audio_format"] return cache_dir / f"{digest}.{extension}" ``` Existing entries are accepted without ownership or file-type validation: ```python if primary_delivery_cache_file.exists(): final_path = output_from_cache( primary_delivery_cache_file, out_path, ) ensure_file_mode(primary_delivery_cache_file, file_mode) if primary_cache_file.exists(): ensure_file_mode(primary_cache_file, file_mode) ensure_file_mode(primary_cache_file, file_mode) ensure_file_mode(final_path, file_mode) ``` Writes and permission changes follow ordinary filesystem path semantics: ```python if not attempt_cache_file.exists(): result = synthesize( api_key=api_key, text=text, voice_id=str(voice["voice_id"]), audio_format=str(settings["audio_format"]), sample_rate=int(settings["sample_rate"]), speed=float(settings["speed"]), volume=float(settings["volume"]), p ...[truncated 3018 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/feishu_audio_sender.py:111
Finding

Automatic Session-Log Recipient Inference Can Send Audio to the Wrong Feishu Chat

Content
View full analysis
tuple[str, str]: if explicit_chat_id: return explicit_chat_id, "" if session_file: path = Path(session_file).expanduser() chat_id = extract_chat_id(path) if chat_id: return chat_id, str(path) raise SystemExit(f"no chat_id found in {path}") sessions_dir = workspace_root / "sessions" candidates = sorted( sessions_dir.glob("agent_main_feishu_direct_*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True, ) if not candidates: candidates = sorted( sessions_dir.glob("*feishu*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True, ) for candidate in candidates: chat_id = extract_chat_id(candidate) if chat_id: return chat_id, str(candidate) state_path = workspace_root / "state" / "state.json" try: state = load_json(state_path) except SystemExit: state = {} last_channel = str(state.get("last_channel") or "").strip() if last_channel.startswith("feishu:"): chat_id = last_channel.split(":", 1)[1].strip() if chat_id: return chat_id, str(state_path) raise SystemExit( "could not infer feishu chat_id from session logs; pass --chat-id" ) ``` The final regex match anywhere in the complete log is accepted: ```python def extract_chat_id(path: Path) -> str: try: text = path.read_text( encoding="utf-8", ...[truncated 3810 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill advertises narrowly scoped voice-reply behavior but also instructs access to local workspace/session/state files and chat inference behavior that is not prominently declared in the purpose statement. That mismatch is dangerous because operators may approve the skill expecting only TTS generation, while it can also inspect local state and perform message-delivery actions with side effects.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The skill advertises narrowly scoped voice-reply behavior but also instructs access to local workspace/session/state files and chat inference behavior that is not prominently declared in the purpose statement. That mismatch is dangerous because operators may approve the skill expecting only TTS generation, while it can also inspect local state and perform message-delivery actions with side effects.

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 33)May include surrounding context.

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
3. Run `scripts/openclaw_voice_switchboard.py`.

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The skill instructs runtime substitution of an injected token with the real sk-... API key from ~/.audioclaw/workspace/state/senseaudio_credentials.json. Accessing a local credential store to retrieve a more privileged secret increases blast radius: a skill that appears to need only a provided token can silently escalate to stored credentials, and compromise of the skill or host process could expose reusable API secrets.

Content

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

md
This skill now treats `SENSEAUDIO_API_KEY` as the default API key source again.

Runtime rules:
- If the host app injects `SENSEAUDIO_API_KEY` as an AudioClaw login token such as `v2.public...`, the shared bootstrap will replace it with the real `sk-...` value from `~/.audioclaw/workspace/state/senseaudio_credentials.json` before TTS starts.
- `--api-key-env` still works, but the default runtime path is `SENSEAUDIO_API_KEY`.

If you need the exact same speaker timbre across many emotions, use a purchased multi-variant voice family or an authorized custom voice. Otherwise this skill will approximate the requested emotion with the best available voice and tuning.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

User-provided text is sent to an external TTS provider, but the code contains no consent, warning, minimization, or policy gate around transmitting potentially sensitive content. In a voice-reply skill this behavior is expected functionally, yet it is still dangerous because agents may pass secrets, personal data, or internal content to a third-party service without explicit disclosure.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
84% confidence
Finding

The code copies the entire parent process environment and forwards it to subordinate scripts, which may include unrelated secrets such as API keys, tokens, proxy credentials, or CI secrets. In a skill/executor context that chains multiple helper scripts, this broad propagation increases the blast radius if a child script logs, exposes, or mishandles environment data.

Content

Scanner excerpt · scripts/picoclaw_voice_reply.py (reported line 78)May include surrounding context.

python
parser.add_argument("--skip-direct-send", action="store_true")
    args = parser.parse_args()

    env = os.environ.copy()
    workspace_root = Path(args.workspace_root).expanduser()
    switchboard_args = [
        "--text", args.text,

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill describes capabilities that require shell execution, network access, environment-variable use, and reads/writes under the local workspace, yet it declares no explicit tool scope or permission boundaries. In an agent setting, that omission can cause over-broad execution authority and makes it harder for a host to sandbox the skill to the minimum necessary privileges.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
- `--out` inside the AudioClaw workspace
   - `--openclaw-workspace-root` pointing at the workspace root
   - `--delivery-profile feishu_voice` when the downstream channel prefers `.ogg/.opus`
   - optional `--chmod 644` if you want to be explicit, though this skill now defaults to `0644`
   - if `--openclaw-workspace-root` is set and `--out` is omitted, this skill now writes to `workspace/state/audio/` automatically
7. Use the returned JSON manifest in AudioClaw to:
   - prefer `scripts/picoclaw_voice_reply.py` for AudioClaw on Feishu

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The instruction says the assistant should send 'one short natural Chinese line' if a final message is required. This imposes a specific language choice without indicating that it depends on the user's language or offering an opt-in, which violates the language/locale policy criteria.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

The skill is explicitly designed to send user-provided text and generated audio to external services, including the SenseAudio API and Feishu endpoints. External transmission is expected for TTS, but it is still security-relevant because sensitive conversation content, voice preferences, and possibly metadata may leave the local environment without clear consent, minimization, or domain restrictions.

Content

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

md
## Resources

- `scripts/senseaudio_tts_client.py`
  - Small importable client for `https://api.senseaudio.cn/v1/t2a_v2`
  - Handles SSE chunks and writes audio bytes
- `references/openclaw_voice_switchboard.md`
  - TTS capability summary plus the official voice catalog reference at `https://senseaudio.cn/docs/voice_api`

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The default prompt uses broad activation language such as "Use $audioclaw-skills-voice-reply to turn an AudioClaw reply into AudioClaw speech" and then sets behavioral defaults for future voice replies. Without a narrowly defined trigger condition, the host agent may invoke this skill more often than intended, causing mode-switching into voice output when the user did not clearly request it and increasing the chance of unintended message handling in Feishu/Lark workflows.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The prompt instructs the agent to send a short natural Chinese line if a final assistant message is required, regardless of the user's language preference. This can override user expectations, create confusing or inaccessible output, and leak implementation-specific behavior into the user interaction when no confirmation or a different language would be more appropriate.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code fetches a tenant token, uploads a local audio file to Feishu, and sends it to a chat, which transmits user data over the network. The script includes no confirmation prompt, no user-facing disclosure before transmission, and no comments/docstrings warning about the privacy impact of uploading local audio and inferred chat context.

Content

No source excerpt is available for this finding.