Is FancyGPT safe?

High risk

FancyGPT sends saved ChatGPT conversation text to its Firebase Storage bucket when its chat editor opens.

When you use FancyGPT to save a ChatGPT thread, it captures the conversation content from the page and stores it locally for rendering in its editor. When that editor opens, the extension converts the rendered conversation to a text file and uploads it to the fancygpt Firebase Storage bucket.

Viszenv1.1.13Chrome Web Store
72Risk
Who publishes it

VISZEN (PTY) LTD - 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
Viszen
Declared legal entity
VISZEN (PTY) LTD
Registered address
18 GREEN ACRES 13 OHARE ST, Centurion 0157, ZA
Registered contact
VISZEN

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

Publishers can request a review.

Findings

ChatGPT conversation text uploads to FancyGPT storage

Clicking FancyGPT's "Make your ChatGPT fancy" button reads conversation turns from the page, saves their HTML/roles in storage, opens its chat viewer, converts the chat to text, and uploads it to Firebase bucket `fancygpt.appspot.com`.

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

You click FancyGPT's "Make your ChatGPT fancy" button while viewing a ChatGPT conversation.

The popup only proceeds when the active tab URL includes chatgpt.com.

The extension did this

The extension reads the visible conversation, stores the messages locally, opens its chat viewer, and uploads a text file of the rendered chat.

The upload happens when `chats.html` loads, before the separate "Save Text file" button is clicked.

Conversation data handled by the upload path
  • Conversation message HTML
    User: Draft an incident update for Monday; Assistant: Here is a concise status note. (illustrative)

    The saved text can include what you wrote to ChatGPT and what ChatGPT returned in that conversation.

  • Message role
    user

    The extension keeps whether each message came from you or from the assistant, preserving the structure of the conversation.

  • Saved thread key
    fancygptthread.thread[0].html

    The conversation is kept in extension storage before the chat viewer turns it into the uploaded text file.

  • Storage object path
    chats/2026-7-13/3b241101-e2bb-4255-8caf-4136c566a962.txt

    The uploaded file is placed in a dated chats folder with a randomly generated text filename.

Captured request
POSThttps://firebasestorage.googleapis.com/v0/b/fancygpt.appspot.com/o
The code that does this

Source path from popup click to Firebase Storage upload

Readable version

Popup click handler injects the page-reading function and opens chats.html

extracted/popup.js
let saveChatElement = document.getElementById("saveChat");saveChatElement.addEventListener("click", async () => {  // Extract thread data  let [tab] = await chrome.tabs.query({active: true, currentWindow:true})  if (tab.url.includes('chatgpt.com')) {    // Save thread    chrome.scripting.executeScript({      target: {tabId: tab.id},      function: saveThread    });    // Open chats page    chrome.tabs.create({ url: "chats.html" });  } else {    document.getElementById("incorrectUrlMessage").classList.remove('d-none');  }});

Injected function collects ChatGPT turns and stores them locally

extracted/popup.js
function saveThread() {  chrome.storage.local.set({'fancygptthread': []}, () => {  });  let chatThread = [];  let chatElements = [];  const selectors = [    'article[data-testid="conversation-turn"]',    '[data-testid="conversation-turn"]',    'div[data-message-author-role]',    'div[data-message-id]'  ];  for (const selector of selectors) {    const found = Array.from(document.querySelectorAll(selector));    if (found.length > 0) {      chatElements = found;      break;    }  }  if (chatElements.length === 0) {    const thread = document.querySelector('#thread');    if (thread) {      const articles = Array.from(thread.querySelectorAll('article'));      if (articles.length > 0) {        chatElements = articles;      }    }  }  for (let i = 0; i < chatElements.length; i++){    let currentElement = chatElements[i];    let contentElement = null;    const contentSelectors = [      '[data-testid="message-content"]',      '[data-message-content="true"]',      '.markdown',      '.prose',      '.whitespace-pre-wrap'    ];    for (const selector of contentSelectors) {      const found = currentElement.querySelector(selector);      if (found) {        contentElement = found;        break;      }    }    const target = contentElement || currentElement;    const targetClone = target.cloneNode(true);    const buttonElement = targetClone.querySelectorAll('button');    if (buttonElement) {      buttonElement.forEach((button) => {        button.remove();      });    }    let role = currentElement.getAttribute('data-message-author-role');    if (!role) {      const roleNode = currentElement.querySelector('[data-message-author-role]');      if (roleNode) {        role = roleNode.getAttribute('data-message-author-role');      }    }    chatThread.push({      html: targetClone.innerHTML,      role: role || null    });  }  let chatObject = {    'thread' : chatThread  }  chrome.storage.local.set({'fancygptthread': chatObject}, () => {  });}

Firebase configuration and upload function

extracted/chats.js
const firebaseConfig = {  apiKey: "AIzaSyDeOs79Zo-ShlDqcIuTqHuXgwxhqdp5M9c",  authDomain: "fancygpt.firebaseapp.com",  projectId: "fancygpt",  storageBucket: "fancygpt.appspot.com",  messagingSenderId: "825047094039",  appId: "1:825047094039:web:738f0b5579d48db49bdd52",  measurementId: "G-3V9N3YJDVE"};function uuidv4() {  return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>      (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)  );}async function saveTextFileToStorage() {  const turndownService = new TurndownService();  let markdown = turndownService.turndown($('#container').html());  markdown = markdown.replaceAll('Exclude message', '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');  markdown = markdown.replaceAll('\n### Type a snippet title here (Optional)\n', '');  markdown = markdown.replaceAll('FancyGPT lets you beautify your threads, and store them for future reference or sharing with others.\n\n1.  Choose a style that you like\n2.  Add an optional snippet title\n3.  Exclude any messages that you don\'t want\n4.  Click and drag on ChatGPT messages to highlight content\n5.  Click "Save Image" to download your fancy snippet as an image\n6.  Click "Save PDF" to download your fancy snippet as a searchable PDF\n7.  Click "Save Text file" to download your fancy snippet as a text file\n\nNote: This extension is reliant on ChatGPT\'s thread format and could break if things change.\n', '');  markdown = markdown.replaceAll('From ChatGPT, designed with sparkles\\_fill FancyGPT', '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nFrom ChatGPT, designed with FancyGPT\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');  const file = new Blob([markdown], { type: 'text/plain' });  firebase.initializeApp(firebaseConfig);  let dateObj = new Date();  let month = dateObj.getUTCMonth() + 1; //months from 1-12  let day = dateObj.getUTCDate();  let year = dateObj.getUTCFullYear();  let newdate = year + "-" + month + "-" + day;  let storageRef = firebase.storage().ref('chats/' + newdate + '/' + uuidv4() + '.txt');  await storageRef.put(file);}
Upload destination
    • firebasestorage.googleapis.com

    Receives the Firebase Storage upload request for bucket fancygpt.appspot.com.

    • fancygpt.appspot.com

    The Firebase Storage bucket configured in the extension source as the upload target.

What it can do

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

  • Store data in your browser

    storage

  • Store an unlimited amount of data in your browser

    unlimitedStorage

  • Act on the current tab, but only after you click the extension

    activeTab

  • Run its own code inside the pages you visit

    scripting

Where it sends data

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

  • firebasestorage.googleapis.com

    FancyGPT sends data to firebasestorage.googleapis.com. 2 other extensions we have analysed send data here.

  • fancygpt.appspot.com

    FancyGPT sends data to fancygpt.appspot.com. No other extension we have analysed sends data here.

Updated 30 September 2026meonalmakdjaojaoipfhahcfccoecegk