From 3eaebae2a3ad84f8ab154b241633de04e7511d60 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Fri, 27 Feb 2026 07:56:49 +0100 Subject: [PATCH] Fix authenticated launch URL anon leak and add audio override diagnostics. Remove lingering anon=true from encoded launcher url parameter in auth mode and log getUserMedia override activation details to verify Teams receives the injected audio stream. Made-with: Cursor --- src/bot/audioProcedure.ts | 11 +++++++++++ src/bot/orchestrator.ts | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/bot/audioProcedure.ts b/src/bot/audioProcedure.ts index cbc573b..95d4565 100644 --- a/src/bot/audioProcedure.ts +++ b/src/bot/audioProcedure.ts @@ -68,6 +68,17 @@ export class AudioProcedure { // Keep the real video tracks (from fake camera) realStream.getVideoTracks().forEach(t => combinedStream.addTrack(t)); + // Diagnostic signal for production logs: confirms override really feeds Teams. + try { + const audioTracks = combinedStream.getAudioTracks(); + const videoTracks = combinedStream.getVideoTracks(); + console.log( + `[AudioPlayback] getUserMedia override active: audioTracks=${audioTracks.length}, videoTracks=${videoTracks.length}, audioLabel="${audioTracks[0]?.label || 'n/a'}"`, + ); + } catch { + // ignore + } + return combinedStream; } diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts index 37631c1..17cac1d 100644 --- a/src/bot/orchestrator.ts +++ b/src/bot/orchestrator.ts @@ -286,6 +286,19 @@ export class BotOrchestrator { try { const urlObj = new URL(launchUrl); urlObj.searchParams.delete('anon'); + // Some Teams launcher URLs carry the real meeting path in an encoded "url" param. + // In auth mode that inner URL can still contain anon=true, which forces guest-like behavior. + const encodedInnerUrl = urlObj.searchParams.get('url'); + if (encodedInnerUrl) { + try { + const inner = new URL(encodedInnerUrl, 'https://teams.microsoft.com'); + inner.searchParams.delete('anon'); + const innerPath = `${inner.pathname}${inner.search}${inner.hash}`; + urlObj.searchParams.set('url', innerPath); + } catch { + // keep original inner URL if parsing fails + } + } launchUrl = urlObj.toString(); } catch { /* keep as-is */ }