Back to skill

Security audit

Discord Voice Using Deepgram

Security checks for vulnerabilities and agentic risk

Overview

This Discord voice skill largely does what it claims, but it needs review because unintended speakers may be captured, transcripts are logged, and voice prompts can reach the agent's full toolset.

Review this before installing on any server with sensitive conversations or powerful agent tools. Prefer Discord user IDs over names, set an explicit allowlist, disable auto-join and voice speaker switching unless needed, isolate the voice agent from high-impact tools, and remove or tightly restrict transcript logging.

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

Error
Location
src/voice-connection.ts:886
Finding

Speaker Authorization Fails Open When the Primary User Cannot Be Resolved

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/voice-connection.ts:541
Finding

Discord Voice Transcripts and User Identifiers Are Written to Application Logs

Content
View full analysis
{ if (isFinal) { this.logger.debug?.(`[discord-voice] Streaming transcript (final): "${text}"`); } else { this.logger.debug?.(`[discord-voice] Streaming transcript (interim): "${text}"`); } }); ``` ```ts this.logger.info(`[discord-voice] Transcribed: "${transcribedText}"`); ``` ```ts async function handleTranscript( userId: string, guildId: string, channelId: string, text: string ): Promise { api.logger.info(`[deepgram-discord-voice] Processing transcript from ${userId}: "${text}"`); ``` ### Technical Analysis The plugin logs full interim and final speech transcripts. The transcript is also logged together with the speaker's Discord user ID in `index.ts`. Voice conversations may contain personal information, private business discussions, authentication data, credentials spoken aloud, or other confidential material. Application logs often have broader access controls and substantially longer retention periods than live voice data. The same transcript can be recorded more than once: - Interim or final streaming transcript at debug level. - Final transcript at information level. - Transcript and Discord user ID when processing begins. Because final transcripts are logged at information level, exposure occurs under ordinary production logging configurations and does not require debug logging to be enabled. ### Attack Path 1. An allowed participant speaks sensitive information in a connected Discord voice channel. 2. The audio is sent to Deepgram and converted into text. 3. The plugin writes the full transcript to the information log. 4. `handleTranscript()` writes the transcript ...[truncated 903 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (31)

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The finding indicates the skill may dynamically load core agent modules, manage sessions/workspaces, access persistent session stores, and support embedded agent execution—capabilities far beyond a voice plugin's declared role. Such hidden breadth materially increases risk because a plugin installed for speech features may also gain privileged access to agent state, persistence, and execution pathways that could be abused for lateral movement or data exposure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The finding indicates the skill may dynamically load core agent modules, manage sessions/workspaces, access persistent session stores, and support embedded agent execution—capabilities far beyond a voice plugin's declared role. Such hidden breadth materially increases risk because a plugin installed for speech features may also gain privileged access to agent state, persistence, and execution pathways that could be abused for lateral movement or data exposure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The finding indicates the skill may dynamically load core agent modules, manage sessions/workspaces, access persistent session stores, and support embedded agent execution—capabilities far beyond a voice plugin's declared role. Such hidden breadth materially increases risk because a plugin installed for speech features may also gain privileged access to agent state, persistence, and execution pathways that could be abused for lateral movement or data exposure.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill declares required environment variables and clearly describes networked Discord/Deepgram behavior, but it does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch weakens policy enforcement and review because the runtime may grant broader env and network access than is transparently documented, increasing the chance of unintended token exposure or outbound communication abuse.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The wake word and example commands indicate that speaker-switching can be controlled by spoken phrases, but the schema text does not define safeguards such as exact speaker verification, confirmation flows, or anti-spoofing constraints. In a Discord voice channel, this can let normal conversation, imitation, replayed audio, or ambiguous name matching trigger authorization changes, causing the bot to listen to or respond to unintended users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The plugin logs raw transcribed speech content together with the user ID, which can capture sensitive conversations, credentials, or personal data spoken in the channel. Voice-chat context makes this especially risky because participants may not expect full-text retention in logs, and logs are often broadly accessible and long-lived.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The transcript handler explicitly tells the embedded agent it has access to all normal tools and skills, and the commented-out lane restriction suggests prior scoping was removed. In a voice-channel context, any user who can speak to the bot may be able to indirectly trigger unrelated powerful capabilities, creating privilege expansion far beyond speech transcription and TTS.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The inline comment indicates a deliberate decision to remove a lane restriction because it might limit tool access, which weakens an apparent security boundary. In combination with untrusted voice transcripts being sent to the agent, this increases the chance that spoken prompts can reach sensitive capabilities that should not be available from a Discord voice session.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code sends audio buffers to an external Deepgram service via a WebSocket connection, which is a network operation that transmits user data. While the file has technical comments and some console logging, it does not include any user-facing warning, confirmation, or explicit disclosure that captured audio is being sent off-system.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The function sends the provided text to Deepgram over HTTPS, which is a network operation transmitting user-supplied content to a third party. In this file there is no confirmation prompt, user-facing log/print, or comment/docstring warning that input text will be sent to an external service.

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 · src/streaming-tts.ts (reported line 47)May include surrounding context.

ts
}

  async transcribe(audioBuffer: Buffer, sampleRate: number): Promise<STTResult> {
    const url = new URL("https://api.deepgram.com/v1/listen");
    url.searchParams.set("model", this.model);
    url.searchParams.set("encoding", "linear16");
    url.searchParams.set("sample_rate", String(sampleRate));

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 · src/stt.ts (reported line 29)May include surrounding context.

ts
}

  async transcribe(audioBuffer: Buffer, sampleRate: number): Promise<STTResult> {
    const url = new URL("https://api.deepgram.com/v1/listen");
    url.searchParams.set("model", this.model);
    url.searchParams.set("encoding", "linear16");
    url.searchParams.set("sample_rate", String(sampleRate));

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 · src/tts.ts (reported line 30)May include surrounding context.

ts
}

  async transcribe(audioBuffer: Buffer, sampleRate: number): Promise<STTResult> {
    const url = new URL("https://api.deepgram.com/v1/listen");
    url.searchParams.set("model", this.model);
    url.searchParams.set("encoding", "linear16");
    url.searchParams.set("sample_rate", String(sampleRate));

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The function sends the provided audio buffer to Deepgram over the network for transcription, which is a privacy-relevant operation involving user/system data. While the file has a brief class comment, it does not provide any user-facing disclosure, confirmation, logging, or warning about external transmission of audio content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The synthesize method sends the provided text to Deepgram's remote API, which can expose user content to a third party. While the code is functionally clear, there is no confirmation prompt, user-facing log, or inline warning indicating that input text leaves the local system.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This component records Discord voice audio, transcribes it via external STT providers, and generates replies via external TTS providers, but this file contains no mechanism to notify or obtain consent from channel participants before their voice data is sent to third-party services. In a voice-chat context, that creates a real privacy and compliance risk because users may be unknowingly recorded and their speech shared outside Discord, potentially violating server expectations, platform policies, or legal consent requirements.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

This manifest file describes the skill as a 'Discord voice channel plugin' with STT and TTS capabilities, but it does not indicate what specific commands, contexts, or conditions activate the skill. In a manifest file, the lack of explicit trigger scope can make invocation behavior ambiguous and increase the chance of unintended activation.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
86% confidence
Finding

Using a caret range for a production dependency allows future installs to resolve to different package versions than were originally tested. That increases supply-chain risk because a compromised or breaking upstream minor/patch release could be pulled into deployments without explicit review.

Content

Scanner excerpt · package.json (reported line 23)May include surrounding context.

json
"deepgram"
  ],
  "dependencies": {
    "@discordjs/voice": "^0.18.0",
    "discord.js": "^14.16.3",
    "libsodium-wrappers": "^0.7.15",
    "opusscript": "^0.0.8",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
86% confidence
Finding

Using a caret range for a production dependency allows future installs to resolve to different package versions than were originally tested. That increases supply-chain risk because a compromised or breaking upstream minor/patch release could be pulled into deployments without explicit review.

Content

Scanner excerpt · package.json (reported line 24)May include surrounding context.

json
],
  "dependencies": {
    "@discordjs/voice": "^0.18.0",
    "discord.js": "^14.16.3",
    "libsodium-wrappers": "^0.7.15",
    "opusscript": "^0.0.8",
    "prism-media": "^1.3.5",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
86% confidence
Finding

Using a caret range for a production dependency allows future installs to resolve to different package versions than were originally tested. That increases supply-chain risk because a compromised or breaking upstream minor/patch release could be pulled into deployments without explicit review.

Content

Scanner excerpt · package.json (reported line 25)May include surrounding context.

json
"dependencies": {
    "@discordjs/voice": "^0.18.0",
    "discord.js": "^14.16.3",
    "libsodium-wrappers": "^0.7.15",
    "opusscript": "^0.0.8",
    "prism-media": "^1.3.5",
    "ws": "^8.18.0"

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
86% confidence
Finding

Using a caret range for a production dependency allows future installs to resolve to different package versions than were originally tested. That increases supply-chain risk because a compromised or breaking upstream minor/patch release could be pulled into deployments without explicit review.

Content

Scanner excerpt · package.json (reported line 26)May include surrounding context.

json
"@discordjs/voice": "^0.18.0",
    "discord.js": "^14.16.3",
    "libsodium-wrappers": "^0.7.15",
    "opusscript": "^0.0.8",
    "prism-media": "^1.3.5",
    "ws": "^8.18.0"
  },

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
86% confidence
Finding

Using a caret range for a production dependency allows future installs to resolve to different package versions than were originally tested. That increases supply-chain risk because a compromised or breaking upstream minor/patch release could be pulled into deployments without explicit review.

Content

Scanner excerpt · package.json (reported line 27)May include surrounding context.

json
"discord.js": "^14.16.3",
    "libsodium-wrappers": "^0.7.15",
    "opusscript": "^0.0.8",
    "prism-media": "^1.3.5",
    "ws": "^8.18.0"
  },
  "clawdbot": {

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
91% confidence
Finding

Using an unpinned range for the production WebSocket library is more concerning because network-facing packages are common attack surfaces and may receive security-relevant updates or, conversely, introduce vulnerable versions into fresh installs. In a Discord voice plugin that relies on real-time network communication, dependency drift in this component can directly affect exposed runtime behavior.

Content

Scanner excerpt · package.json (reported line 28)May include surrounding context.

json
"libsodium-wrappers": "^0.7.15",
    "opusscript": "^0.0.8",
    "prism-media": "^1.3.5",
    "ws": "^8.18.0"
  },
  "clawdbot": {
    "extensions": [

Unverifiable Dependency: ws has 7 known advisory(ies) (CVE-2016-10518 (Remote Memory Disclosure in ws); CVE-2024-37890 (ws affected by a DoS when handling a request with many HTTP headers); CVE-2026-45736 (ws: Uninitialized memory disclosure) +4 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The manifest references ws without pinning an exact version, and the package family has multiple published advisories, so there is no reliable way to determine from this file whether installs will avoid affected releases. In a network-facing Discord voice plugin, an unresolved advisory state on a WebSocket stack can expose the service to denial of service or information disclosure depending on the resolved version.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 36)May include surrounding context.

json
]
  },
  "devDependencies": {
    "@sinclair/typebox": "^0.34.48",
    "@types/node": "^25.0.10",
    "@types/ws": "^8.18.0",
    "typescript": "^5.9.3"