Is Video to MP3 safe?

High risk

Video to MP3 uploads user-selected converted audio for AI transcription.

When a user chooses the transcription feature after converting a video, the extension sends the converted MP3 audio to a Supabase edge function. That function forwards the audio to Pollinations for AI transcription, even though the packaged privacy page says conversions stay local and third-party services are not used.

Nikusonv2.0Chrome Web Store
72Risk

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Converted MP3 audio is uploaded for transcription

Pressing Transcribe on a converted file, the extension reads the MP3, encodes it, and sends it to a Supabase endpoint that forwards the audio to Pollinations, though the privacy page says files stay on-device and no third parties are used.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You click Transcribe after a video has been converted to an MP3 file.

The button is shown next to the completed conversion result in the popup.

The extension did this

The extension uploads the converted audio content to a remote transcription endpoint.

The packaged server-function source forwards that audio data to Pollinations for transcription.

Fields sent for transcription
  • Selected audio content
    base64-encoded MP3 audio from a converted file

    Audio from your selected file is sent for remote transcription, so spoken words or background audio can leave your device.

  • Audio format
    mp3

    This tells the transcription service how to interpret the uploaded audio file.

Captured request
POSThttps://sduzciunliptkkkezbzj.supabase.co/functions/v1/transcribe

Dynamic analysis confirmed this request after the Transcribe action; the bundled function returns JSON containing a transcription on success.

Headers
apikey
<redacted>
Content-Type
application/json
Authorization
Bearer <redacted>
The code that does this

Popup code turns the converted MP3 into an upload

Readable version

Transcribe button handler

popup.js
function createConversionItem(file, fileId) {  const conversionItem = conversionTemplate.content.cloneNode(true).querySelector('.conversion-item');  conversionItem.id = `conversion-${fileId}`;  const fileName = conversionItem.querySelector('.file-name');  const displayName = file.name.split('\\').pop().split('/').pop();  fileName.textContent = displayName;  fileName.title = displayName;  const fileSize = conversionItem.querySelector('.file-size');  fileSize.textContent = formatFileSize(file.size);  const removeBtn = conversionItem.querySelector('.remove-btn');  removeBtn.addEventListener('click', () => removeConversion(fileId));  conversionsContainer.appendChild(conversionItem);  const downloadBtn = conversionItem.querySelector('.download-btn');  downloadBtn.addEventListener('click', () => downloadConvertedFile(fileId));  const transcribeBtn = conversionItem.querySelector('.transcribe-btn');  transcribeBtn.addEventListener('click', () => runIfProOrPrompt(() => transcribeAudio(fileId)));  const highQualityBtn = conversionItem.querySelector('.high-quality-btn');  highQualityBtn.addEventListener('click', () => runIfProOrPrompt(() => showFormatModal(fileId)));  return conversionItem;}

MP3 blob encoded and posted

popup.js
async function transcribeAudio(fileId) {  if (!activeConversions.has(fileId)) return;  const conversionInfo = activeConversions.get(fileId);  if (conversionInfo.status !== 'completed' || !conversionInfo.result) return;  const conversionItem = document.getElementById(`conversion-${fileId}`);  if (!conversionItem) return;  openTranscribeModal();  try {    const mp3Blob = conversionInfo.result;    const base64Audio = await blobToBase64(mp3Blob);    const response = await fetch(`${SUPABASE_URL}/functions/v1/transcribe`, {      method: 'POST',      headers: {        'Content-Type': 'application/json',        'apikey': SUPABASE_KEY,        'Authorization': `Bearer ${SUPABASE_KEY}`,      },      body: JSON.stringify({        audioData: base64Audio,        audioFormat: 'mp3'      })    });    if (!response.ok) {      const errorText = await response.text();      throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`);    }    const result = await response.json();    if (result.error) throw new Error(result.error);    const transcription = result.transcription || 'No transcription available';    currentTranscription = transcription;    currentTranscriptionFileName = conversionInfo.fileName;    showTranscriptionResult(transcription);  } catch (error) {    alert(`Ошибка транскрибации: ${error.message}`);    closeTranscribeModal();  }}function blobToBase64(blob) {  return new Promise((resolve, reject) => {    const reader = new FileReader();    reader.onload = () => {      const base64 = reader.result.split(',')[1];      resolve(base64);    };    reader.onerror = reject;    reader.readAsDataURL(blob);  });}
The code that does this

Bundled Supabase function forwards the same audio to Pollinations

Readable version
const POLLINATIONS_URL = "https://text.pollinations.ai/openai";serve(async (req) => {  if (req.method === 'OPTIONS') {    return new Response('ok', {      headers: {        'Access-Control-Allow-Origin': '*',        'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',        'Access-Control-Allow-Methods': 'POST, OPTIONS'      }    });  }  const body = await req.json();  const { audioData, audioFormat } = body;  if (!audioData || !audioFormat) {    return new Response(JSON.stringify({ error: 'Missing audioData or audioFormat' }), { status: 400 });  }  if (!audioData.match(/^[A-Za-z0-9+/]*={0,2}$/)) {    return new Response(JSON.stringify({ error: 'Invalid base64 audio data format' }), { status: 400 });  }  if (audioData.length > 10 * 1024 * 1024) {    return new Response(JSON.stringify({ error: 'Audio file too large. Please use a shorter audio file.' }), { status: 400 });  }  const payload = {    model: "gpt-4o-mini-audio-preview",    messages: [      {        role: "user",        content: [          { type: "input_text", text: "Transcribe this audio and return only the transcript text." },          {            type: "input_audio",            input_audio: {              data: audioData,              format: audioFormat,            },          },        ],      },    ],  };  const response = await fetch(POLLINATIONS_URL, {    method: 'POST',    headers: {      'Content-Type': 'application/json',      'Authorization': `Bearer ${POLLINATIONS_TOKEN}`    },    body: JSON.stringify(payload),  });  const result = await response.json();  const transcription = result?.choices?.[0]?.message?.content;  return new Response(JSON.stringify({ transcription }));});
External hosts involved in the transcription path
    • sduzciunliptkkkezbzj.supabase.co

    Receives the extension popup's transcription POST containing the base64 MP3 audio and mp3 format marker.

    • text.pollinations.ai

    Receives the packaged function's OpenAI-compatible transcription request with input_audio.data set to the same audioData value.

The code that does this

Packaged privacy text says files stay local

Readable version
The packaged privacy page states that the extension does not collect, store, or transmit personal data; says all conversions occur locally; says files never leave the device; says files are not uploaded to external servers or third-party services; and says the extension does not integrate with or rely on third-party services.

What it can do

Permissions this extension asks for, as declared in version 2.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on sduzciunliptkkkezbzj.supabase.co

    https://sduzciunliptkkkezbzj.supabase.co/*

  • Read and change your data on nikuson-mp3-b08f.twc1.net

    https://nikuson-mp3-b08f.twc1.net/*

  • Store data in your browser

    storage

Where it sends data

Destinations our analysis observed Video to MP3 contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • sduzciunliptkkkezbzj.supabase.co

    Video to MP3 sends data to sduzciunliptkkkezbzj.supabase.co. No other extension we have analysed sends data here.

  • text.pollinations.ai

    Video to MP3 sends data to text.pollinations.ai. No other extension we have analysed sends data here.

Updated 30 September 2026eimjdiflifagjigopgmgbnihlocgfemp