Is Video Downloader Social safe?

Medium risk

Video Downloader is medium risk. The shipped service worker captures the outgoing header array from X.com API requests and stores it as twitter_token locally. A later function rebuilds those headers and sends a GET to X.com's timeline API using the saved auth headers.

Downloader Webv0.0.9Chrome Web Store
45Risk

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

X.com authentication headers are saved and reused

The shipped service worker captures the outgoing header array from X.com API requests and stores it as twitter_token locally.

A later function rebuilds those headers and sends a GET to X.com's timeline API using the saved auth headers.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You browse X.com while signed in.

The listener is scoped to X.com API URLs under /i/api/.

The extension did this

The extension saves the outgoing request headers and later uses them in its own X.com API request.

Those headers can include authorization, CSRF, cookie, and other session-related fields sent by the browser.

02EvidenceFIELD TABLE
Header fields stored under twitter_token
FieldValueWhy it matters
Authorization header
Authorization: Bearer <redacted> (illustrative)This value can make an API request act like it came from your signed-in X.com session.
CSRF token
x-csrf-token: 8f7b3c2e91a046d4b5c6a7e8f9012345 (illustrative)This token is part of the browser session context that X.com expects on authenticated requests.
X.com cookies
auth_token=<redacted>; ct0=<redacted> (illustrative)Cookies can identify the signed-in browser session used for the API request.
Other request headers
x-twitter-active-user: yes (illustrative)The stored array preserves the rest of the browser request context that was sent with the X.com API call.
03EvidenceSTORAGE DUMP
What's stored on your device

The extension keeps a reusable copy of session headers in its own storage, not just letting the browser send them with the original request.

Locationchrome.storage.local key 'twitter_token'
Contents (JSON)
{
  "twitter_token": [
    {
      "name": "Authorization",
      "value": "Bearer <redacted>"
    },
    {
      "name": "x-csrf-token",
      "value": "8f7b3c2e91a046d4b5c6a7e8f9012345"
    },
    {
      "name": "Cookie",
      "value": "auth_token=<redacted>; ct0=<redacted>"
    }
  ]
}
04EvidenceNETWORK CAPTURE
Captured request
GEThttps://api.x.com/2/timeline/conversation/${t}.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_composer_source=true&include_ext_alt_text=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&send_error_codes=true&simple_quoted_tweets=true&count=20&ext=mediaStats%252ChighlightedLabel%252CcameraMoment
Headers
Cookieauth_token=<redacted>; ct0=<redacted>
x-csrf-token<redacted>
AuthorizationBearer <redacted>
05EvidenceCODE COMPARE
The code that does this

The worker stores X.com API headers and reuses them in a later fetch

What it actually does
Readable header capturedeobfuscated/js/worker.js
chrome.webRequest.onBeforeSendHeaders.addListener((e => {
  e.requestHeaders && chrome.storage.local.set({
    twitter_token: e.requestHeaders
  })
}), {
  urls: ["*://*.x.com/i/api/*"]
}, ["requestHeaders"])
Readable header replaydeobfuscated/js/worker.js
async fetchDataFromTwitId(t, a, s) {
  const o = `https://api.x.com/2/timeline/conversation/${t}.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_composer_source=true&include_ext_alt_text=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&send_error_codes=true&simple_quoted_tweets=true&count=20&ext=mediaStats%252ChighlightedLabel%252CcameraMoment`,
    n = (await chrome.storage.local.get("twitter_token")).twitter_token.reduce(((e, {
      name: t,
      value: a
    }) => ({
      ...e,
      [t]: a
    })));
  fetch(o, {
    method: "GET",
    headers: n
  }).then((e => e.json())).then((s => {
    if (s.errors) return;
    const o = s.globalObjects.tweets[t].extended_entities.media[0].video_info.variants,
      n = {
        fileName: `Video by ${s.globalObjects.users[s.globalObjects.tweets[t].user_id_str].name} (${t})`,
        url: o.filter((e => "video/mp4" === e.content_type))[0],
        ext: ".mp4"
      };
    e.addVideoLinks([n], a.tab.id, a.tab.url)
  }))
}
06EvidenceTHIRD PARTY LIST
Destination used by the replay function
  • api.x.com

    Receives the extension's conversation timeline GET request with the headers reconstructed from twitter_token.

Updated 17 September 2026jbmbplbpgcpooepakloahbjjcpfoegji