Is YoutubeDigest: summarize and translate using ChatGPT safe?

Medium risk

YoutubeDigest is medium risk. Signing in with Google or Twitter, the extension reads the OAuth code from the redirect and POSTs it to youtubedigest.app/api/exchange, which returns access and refresh tokens instead of the OAuth provider doing so directly.…

theaibigfanv2.4.1.1Chrome Web Store
45Risk
Who publishes it

theaibigfan - no other listings under this identity, 2 shared hostnames

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

Shared hosts - 2 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

youtubedigest.app
Also called by 1 other listing: Youtube ChatGPT summarization
webapp.chatgpt4google.com
Also called by 4 other listings, including Youtube ChatGPT summarization, ChatGPT-The Future - Your AI assistant that can help you anytime, anywhere.

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-522
SourceAI SANDBOX

OAuth codes are exchanged through youtubedigest.app

Signing in with Google or Twitter, the extension reads the OAuth code from the redirect and POSTs it to youtubedigest.app/api/exchange, which returns access and refresh tokens instead of the OAuth provider doing so directly.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You sign in with Google or Twitter through the extension.

The flow uses the browser identity API to open the provider's OAuth page.

The extension did this

The extension sends the returned authorization code to youtubedigest.app for token exchange.

The response is stored locally as access and refresh tokens for later authenticated requests.

02EvidenceFIELD TABLE
Credential fields handled by the exchange path
FieldValueWhy it matters
OAuth authorization code
4/0AeaYSHB7eZQ2ExampleAuthCodeForSignInThis short-lived value is exchanged for tokens that let the extension act as your signed-in account within the requested scope.
Access token
ya29.a0AfH6SMAExampleAccessTokenValueThis token lets the extension make authenticated requests after the sign-in flow completes.
Refresh token
1//0gExampleRefreshTokenForOfflineAccessThis token can be used to obtain new access tokens after the original access token expires.
Signed-in account identity
alex@example.comThe extension stores account details so later requests can be tied to your signed-in profile.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://www.youtubedigest.app/api/exchange
Source code expects JSON containing accessToken and refreshToken.
Headers
Content-Typeapplication/json
04EvidenceCODE COMPARE
The code that does this

Google OAuth code is posted to youtubedigest.app

What it actually does
Readable Google sign-in functionbackground.js
var jn = "1015583372301-ifdpu597snn6mtkveq81da9fsk3jmof7.apps.googleusercontent.com",
  Dn = "https://www.googleapis.com/auth/userinfo.email",
  me = async (r, e) => {
    let o = oe.default.identity.getRedirectURL(),
      a = `https://accounts.google.com/o/oauth2/auth?client_id=${jn}&access_type=offline&response_type=code&redirect_uri=${encodeURIComponent(o)}&scope=${encodeURIComponent(Dn)}&prompt=consent`,
      n = await oe.default.identity.launchWebAuthFlow({
        interactive: r,
        url: a
      });
    if (n) {
      let d = new URL(n).searchParams.get("code"),
        s = await (await fetch("https://www.youtubedigest.app/api/exchange", {
          method: "POST",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            authCode: d
          })
        })).json(),
        p = s.accessToken,
        i = s.refreshToken;
      if (p) {
        let l = p,
          T = await (await fetch(`https://www.googleapis.com/oauth2/v2/userinfo?access_token=${l}`)).json(),
          w = T.email;
        await oe.default.storage.local.set({
          gAuth: {
            gAccessToken: l,
            gRefreshToken: i,
            userEmail: w
          }
        }), e && e(l, T)
      } else console.log("Error signing in: access token not found in response URL", n, s)
    } else console.log("Error signing in: user did not grant permission")
  }
05EvidenceCODE COMPARE
The code that does this

Twitter OAuth code uses the same exchange endpoint

What it actually does
Readable Twitter sign-in functionbackground.js
Wt = async (r, e) => {
  let o = "ZkwzTm5HV0pFVG15cElDR0c3eWc6MTpjaQ",
    a = de.default.identity.getRedirectURL(),
    n = `https://twitter.com/i/oauth2/authorize?response_type=code&client_id=${o}&redirect_uri=${encodeURIComponent(a)}&scope=tweet.write%20users.read%20tweet.read%20offline.access&state=state&code_challenge=challenge&code_challenge_method=plain`,
    f = await de.default.identity.launchWebAuthFlow({
      interactive: r,
      url: n
    });
  if (f) {
    let g = new URL(f).searchParams.get("code"),
      p = await (await fetch("https://www.youtubedigest.app/api/exchange", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          authCode: g,
          type: "twitter"
        })
      })).json(),
      i = p.accessToken,
      l = p.refreshToken;
    if (i) {
      let x = i,
        w = await (await fetch(`https://www.youtubedigest.app/api/twitter/user?accessToken=${i}`)).json();
      await de.default.storage.local.set({
        twitterAuth: {
          twitterAccessToken: x,
          twitterRefreshToken: l,
          username: w.data.username,
          id: w.data.id,
          name: w.data.name
        }
      }), e && e(x, w)
    } else console.log("Error signing in: access token not found in response URL", f, p)
  } else console.log("Error signing in: user did not grant permission")
}
06EvidenceCODE COMPARE
The code that does this

Options-page sign-in button reaches the background handler

What it actually does
Options page sends the sign-in messageoptions.js
bt = ke(async () => {
  Le && Le.postMessage({
    type: "SIGN_IN"
  })
}, [Le])
Background message handler invokes the Google auth helperbackground.js
else if (e.type === "SIGN_IN") await ho((o, a) => {
  r.postMessage({
    type: "SIGNED_IN",
    email: a.email
  })
});
else if (e.type === "TWITTER_SIGN_IN") await Wt(!0, (o, a) => {
  r.postMessage({
    type: "TWITTER_SIGNED_IN",
    userInfo: a
  })
});
else if (e.type === "SILENT_SIGN_IN") {
  try {
    let o = await U.default.storage.local.get("gAuth");
    if (o && o.gAuth) {
      let a = o.gAuth.userEmail;
      r.postMessage({
        type: "SIGNED_IN",
        email: a
      });
      return
    }
  } catch (o) {
    console.log("failed in getting user identity from storage", o)
  }
  await me(!1, (o, a) => {
    r.postMessage({
      type: "SIGNED_IN",
      email: a.email
    })
  })
}
07EvidenceTHIRD PARTY LIST
Hosts involved in the credential flow
  • www.youtubedigest.app

    Receives the OAuth authorization code and returns access and refresh tokens to the extension.

  • accounts.google.com

    Hosts the Google OAuth authorization page opened by the extension.

  • twitter.com

    Hosts the Twitter OAuth authorization page opened by the extension.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

ChatGPT Session Token Extracted and Used for API Access

YoutubeDigest fetches your ChatGPT session token from chatgpt.com/api/auth/session for summaries. 9 captured GETs returned a short-lived accessToken, cached 10s, used against chatgpt.com/backend-api/conversation.

Not sent to the developer.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You request a video summary using the ChatGPT summarization option while logged into ChatGPT.

The extension did this

The extension reads your active ChatGPT session token from chatgpt.com/api/auth/session without asking for explicit authorization, then uses it to make API calls on your behalf.

Our dynamic analysis captured 9 successful GET requests to this endpoint during a single session (dynamic analysis).

02EvidenceCODE COMPARE
The code that does this

Session token extraction, as shipped in background.js

What it actually does
Session token extraction with 10-second cachebackground.js
const TOKEN_CACHE_KEY = 'accessToken';
const tokenCache = new LRUCache(10 * 1000); // 10-second TTL

async function fetchChatGptToken() {
 // Return cached token if still valid
 if (tokenCache.get(TOKEN_CACHE_KEY)) return tokenCache.get(TOKEN_CACHE_KEY);

 // Fetch the authenticated session endpoint — no explicit user consent prompt
 const response = await fetch('https://chatgpt.com/api/auth/session');
 if (response.status === 403) throw new Error('CLOUDFLARE');

 const session = await response.json().catch(() => ({}));
 if (!session.accessToken) throw new Error('UNAUTHORIZED');

 // Cache and return the JWT
 tokenCache.set(TOKEN_CACHE_KEY, session.accessToken);
 return session.accessToken;
}
03EvidenceNETWORK CAPTURE
Captured request
GEThttps://chatgpt.com/api/auth/session
HTTP 200. Response body contains 'accessToken': 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjE...' (JWT). dynamic analysis captured 9 such requests from the background service worker.
Headers
Cookie__Secure-next-auth.session-token=<session-cookie>
04EvidenceTHIRD PARTY LIST
Where the session token is transmitted:
  • chatgpt.com

    Session token is fetched from /api/auth/session and then used as a Bearer token in POST requests to /backend-api/conversation containing the YouTube video transcript.

What it can do

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

    https://*.openai.com/

  • Sign you in with your Google account

    identity

  • Store data in your browser

    storage

Updated 30 September 2026agjkjablkiapmpbeglmdcmhnihlofija