T09 · Insecure Skill Coding Practices
- Location
scripts/common.js:13- Finding
Unvalidated Azure Speech Endpoint Can Expose Credentials and Audio
- Content
View full analysis
Vulnerability Details
File Location:
scripts/common.js:13-15andscripts/transcribe.js:51-60
Vulnerability Type: Unrestricted transmission of secrets and sensitive audio to a configurable endpoint
Risk Level: MediumVulnerable Code
From
scripts/common.js:13-15:js function apiBase() { return requiredEnv('AZURE_SPEECH_ENDPOINT').replace(/\/$/, ''); }From
scripts/transcribe.js:51-60:js const url = `${apiBase()}/speechtotext/transcriptions:transcribe?api-version=${encodeURIComponent(args['api-version'] || apiVersion())}`; const res = await fetch(url, { method: 'POST', headers: { 'Ocp-Apim-Subscription-Key': apiKey(), }, body: form, });Technical Analysis
The value of
AZURE_SPEECH_ENDPOINTis used directly as the request origin without parsing or validating its scheme, hostname, port, embedded credentials, or path. The resulting request includes the Azure Speech subscription key in theOcp-Apim-Subscription-Keyheader and the complete audio recording in the multipart request body.Consequently, anyone able to modify the process environment can redirect the request to an attacker-controlled server. The implementation also accepts a plain HTTP endpoint, which could expose both the credential and audio to network interception.
This issue does not independently grant an attacker control over the environment. Exploitation requires poisoning, misconfiguration, or unauthorized modification of
AZURE_SPEECH_ENDPOINT.Attack Path
- An attacker gains the ability to influence the environment used to launch the skill, such as through a compromised configuration, deployment secret, wrapper script, or shell environment.
- The attacker sets
AZURE_SPEECH_ENDPOINTto an attacker-controlled HTTP or HTTPS origin. - A user or agent invokes
scripts/transcribe.jswith an audio file. - The script reads the complete audio file and constructs a multipart request.
- The script sends the audio and `AZUR ...[truncated 892 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse the configured endpoint with the standard
URLclass and reject malformed values. - Require the
https:protocol. Permit plain HTTP only behind a clearly named, explicit development-only opt-in. - Restrict production requests to documented Azure Speech hostname suffixes or a deployment-specific trusted-host allowlist.
- Reject embedded usernames or passwords, unexpected ports, query strings, fragments, and unapproved base paths.
- Construct the API URL from validated URL components rather than concatenating an unchecked string.
- Consider separating support for custom endpoints behind an explicit option that warns that the subscription key and audio will be disclosed to that destination.
- Apply least privilege to the Azure key, rotate it if endpoint poisoning is suspected, and prefer short-lived identity-based authentication where the service supports it.
- Add automated tests covering attacker-controlled hosts, plain HTTP, deceptive suffixes, embedded credentials, unexpected ports, and malformed URLs.
Example validation pattern:
js function apiBase() { const endpoint = new URL(requiredEnv('AZURE_SPEECH_ENDPOINT')); if (endpoint.protocol !== 'https:') { throw new Error('AZURE_SPEECH_ENDPOINT must use HTTPS'); } const host = endpoint.hostname.toLowerCase(); const allowedSuffixes = [ '.cognitiveservices.azure.com', '.api.cognitive.microsoft.com', ]; if (!allowedSuffixes.some(suffix => host.endsWith(suffix))) { throw new Error('AZURE_SPEECH_ENDPOINT is not an approved Azure Speech host'); } if ( endpoint.username || endpoint.password || endpoint.port || endpoint.search || endpoint.hash ) { throw new Error('AZURE_SPEECH_ENDPOINT contains unsupported components'); } return endpoint.origin; }The final allowlist should be verified against the Azure Speech endpoint formats officially supported by the deployment, including any required sovereign-clou ...[truncated 31 chars]
- Parse the configured endpoint with the standard
