Back to skill

Security audit

AudioClaw Skills Voice Intake

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches a voice-transcription purpose, but it uses unbundled runtime code, local credential bootstrap, and a hard-coded cloned voice handoff that require review before installation.

Install only if you trust the AudioClaw/SenseAudio environment, accept that user voice recordings are sent to the documented remote ASR service, and can constrain the runtime to trusted shared modules. Review or remove the cloned voice handoff and make credential lookup explicit before using this in production.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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)

T01 · Skill Instruction Hijacking

Warning
Location
agents/openai.yaml:4
Finding
Cross-Skill Instruction Hijacking Through a Hard-Coded Cloned Voice Identity<![CDATA[ ## Vulnerability Details **File Location**: `agents/openai.yaml:4` **Vulnerability Type**: Cross-Skill agent instruction redirection **Risk Level**: Medium ### Vulnerable Code ```yaml default_prompt: "Use $audioclaw-skills-voice-intake to transcribe a user voice message with AudioClaw and package it for AudioClaw. If the user is in ongoing voice-reply mode, hand off to $audioclaw-skills-voice-reply and prefer the prepared clone voice_id `vc-yxdCFUKyNLPexxJ66jaXWk` unless the user asked for another voice." ``` ### Technical Analysis The default prompt instructs the agent to invoke a separate voice-reply Skill and prefer a specific cloned voice identity. This behavior exceeds the stated speech-to-text intake function and conflicts with `SKILL.md`, which explicitly states that the Skill should not be used for speech output. Because the instruction is loaded as part of the Skill's agent-facing configuration, it can influence the current session without the user explicitly selecting the cloned voice. The fixed voice identifier may also represent a particular person's cloned voice, creating consent and impersonation concerns. This is instruction hijacking rather than direct local code execution: it changes downstream agent behavior and redirects processing to another Skill. ### Attack Path 1. A user or channel supplies a voice message and triggers the voice-intake Skill. 2. The agent loads the default prompt from `agents/openai.yaml`. 3. If the session is interpreted as being in ongoing voice-reply mode, the prompt directs the agent to hand off to `$audioclaw-skills-voice-reply`. 4. The downstream Skill is instructed to use the embedded cloned voice ID unless the user explicitly requests a different voice. 5. Speech may consequently be generated with a voice that the user did not explicitly select or authorize. ### Impact Assessment This issue does not directly grant operating-system privileges. Its scope is the agent's active session and downst ...[truncated 363 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the automatic handoff and hard-coded cloned voice identifier from the voice-intake Skill. 2. Keep speech-output instructions exclusively in the dedicated voice-reply Skill. 3. Require explicit user confirmation before selecting or using any cloned voice. 4. Resolve voice identities from an authorized, user-specific configuration rather than embedding a global identifier in an agent prompt. 5. Clearly disclose the selected voice before generating speech and provide a straightforward opt-out. 6. Add policy enforcement in the voice-reply component to verify that the requesting user is authorized to use the selected cloned voice. 7. Restrict the intake Skill's default prompt to transcription, clarification, and construction of the documented user-turn payload. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/openclaw_voice_intake.py:10
Finding
Arbitrary Code Execution Through Ancestor-Controlled Python Module Loading<![CDATA[ ## Vulnerability Details **File Location**: `scripts/openclaw_voice_intake.py:10-23` **Vulnerability Type**: Local module and tool hijacking through unsafe dynamic import resolution **Risk Level**: High ### Vulnerable Code ```python def _bootstrap_shared_senseaudio_env() -> None: current = Path(__file__).resolve() for parent in current.parents: candidate = parent / "_shared" / "senseaudio_env.py" if candidate.exists(): candidate_dir = str(candidate.parent) if candidate_dir not in sys.path: sys.path.insert(0, candidate_dir) from senseaudio_env import ensure_senseaudio_env ensure_senseaudio_env() return _bootstrap_shared_senseaudio_env() ``` ### Technical Analysis The bootstrap routine searches every ancestor of the script for `_shared/senseaudio_env.py`. When it finds a matching file, it inserts that file's directory at the beginning of `sys.path` and imports `senseaudio_env`. Python executes module-level code during import. Therefore, a matching file does not need to provide a legitimate implementation before its payload runs. Any party capable of placing or modifying `_shared/senseaudio_env.py` under a searched ancestor directory can execute arbitrary Python code when the Skill starts. Prepending the discovered directory to `sys.path` also gives that location priority in subsequent module resolution. This can facilitate additional dependency spoofing if imports use names that exist in the attacker-controlled directory. The dynamically loaded implementation is outside the audited project. Consequently, the effective runtime behavior—including the documented credential bootstrap—is not fully represented by the reviewed package. The script also imports `senseaudio_api_guard`, which is absent from the supplied project, further demonstrating reliance on unbundled runtime components. Exploitation depends on write access to a searched ancestor loc ...[truncated 1911 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove ancestor-directory scanning and stop modifying `sys.path` at runtime. 2. Bundle `senseaudio_env` and `senseaudio_api_guard` inside the reviewed package. 3. Use explicit package-relative imports, for example: ```python from .senseaudio_env import ensure_senseaudio_env from .senseaudio_api_guard import ensure_runtime_api_key ``` 4. Package the scripts as a proper Python module with a fixed, auditable import root. 5. Fail closed if a required trusted module is unavailable rather than searching parent directories for a replacement. 6. Pin and integrity-check externally distributed dependencies using hashes or signed release artifacts. 7. Ensure the installation directory and all parent directories are not writable by untrusted users. 8. Avoid placing shared executable Python modules in broadly writable locations such as temporary or multi-user workspace directories. 9. Add startup validation that verifies imported modules originate from the expected package directory. 10. Include all credential-handling code in the audit scope, especially any code that reads `~/.audioclaw/workspace/state/senseaudio_credentials.json`. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (11)

Credential Access

High
Category
Privilege Escalation
Content
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 ASR starts.
- `--api-key-env` still works, but the default runtime path is `SENSEAUDIO_API_KEY`.

## Commands
Confidence
93% confidence
Finding
The skill instructs runtime bootstrap logic to replace a provided token with a real API key loaded from a local credentials file under the user's home directory. Accessing a persistent local secrets store as part of skill execution expands credential exposure and creates a path for unintended secret use or leakage, especially in multi-skill or multi-tenant agent environments.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill describes capabilities that involve shell execution, filesystem access, environment variable use, and outbound network calls, but it does not declare any explicit tool scope or permissions boundary. That omission can cause the skill to run with broader-than-expected privileges, making accidental misuse or unsafe composition with other agent capabilities more likely.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The default prompt instructs the agent to prefer a specific prepared clone voice_id for voice replies without requiring explicit user opt-in at the point of use. Hard-coding a cloned voice preference can cause unauthorized voice impersonation, consent violations, or privacy/compliance issues if the selected voice belongs to a real person or is used outside the user's expectations.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The natural-language fallback message `这段语音我没有听清楚,请再说一次。` is always used when clarification is needed, regardless of the user's preferred language or locale. This imposes a specific language choice with no opt-in or documented locale constraint.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This script sends user audio and associated request parameters to a remote ASR service via transcribe(request, api_key) and records the API endpoint in the manifest, but provides no in-file notice, consent gate, or minimization around remote transmission. In a voice-intake skill, this can expose sensitive spoken content, identifiers, and metadata to an external service without clear user awareness.

Tainted flow: 'manifest' from os.getenv (line 129, credential/environment) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Content
if args.out_json:
        out_path = Path(args.out_json)
        out_path.parent.mkdir(parents=True, exist_ok=True)
        out_path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")

    json.dump(manifest, sys.stdout, ensure_ascii=False, indent=2)
    sys.stdout.write("\n")
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
from typing import Dict, List, Optional, Sequence, Tuple, Union


API_URL = "https://api.senseaudio.cn/v1/audio/transcriptions"
SUPPORTED_SUFFIXES = {".wav", ".mp3", ".ogg", ".opus", ".flac", ".aac", ".m4a", ".mp4"}
MAX_BYTES = 10 * 1024 * 1024
MAX_SECONDS = 7200
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
from typing import Dict, List, Optional, Sequence, Tuple, Union


API_URL = "https://api.senseaudio.cn/v1/audio/transcriptions"
SUPPORTED_SUFFIXES = {".wav", ".mp3", ".ogg", ".opus", ".flac", ".aac", ".m4a", ".mp4"}
MAX_BYTES = 10 * 1024 * 1024
MAX_SECONDS = 7200
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
from typing import Dict, List, Optional, Sequence, Tuple, Union


API_URL = "https://api.senseaudio.cn/v1/audio/transcriptions"
SUPPORTED_SUFFIXES = {".wav", ".mp3", ".ogg", ".opus", ".flac", ".aac", ".m4a", ".mp4"}
MAX_BYTES = 10 * 1024 * 1024
MAX_SECONDS = 7200
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def detect_duration_seconds(path: Path) -> float:
    try:
        output = subprocess.check_output(["/usr/bin/afinfo", str(path)], stderr=subprocess.STDOUT)
    except (subprocess.CalledProcessError, FileNotFoundError):
        return 0.0
    for raw_line in output.decode("utf-8", "ignore").splitlines():
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The code uploads raw audio content to a third-party ASR endpoint, but this file contains no mechanism to ensure the caller has obtained user consent or surfaced a privacy disclosure before transmission. Because voice recordings often contain sensitive personal data, silent external transfer can create privacy, compliance, and data-governance exposure in the skill context.

Static analysis

No suspicious patterns detected.