Is Video Speed Controller safe?

High risk

Video Speed Controller is high risk. Each time the service worker starts, it contacts the backend and downloads a list of domains to monitor, stored locally. This list controls which sites have their URLs tracked, and can be expanded anytime without an update.…

Video Speed Controllerv3.2.7Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Server-controlled list of monitored sites fetched at every startup

Each time the service worker starts, it contacts the backend and downloads a list of domains to monitor, stored locally.

This list controls which sites have their URLs tracked, and can be expanded anytime without an update.

01EvidenceCAUSE EFFECT
What actually happens
You did this

The extension's service worker starts (browser launch or service-worker wake).

Top-level code in background.js runs unconditionally.

The extension did this

The extension fetches a list of domains to monitor from backend.videospeeder.com and stores it locally.

The server-supplied array is saved as platformList in chrome.storage.local, governing which sites will have their URLs forwarded on subsequent navigations.

02EvidenceCODE COMPARE
The code that does this

Platform list fetch on startup, background.js lines 274-303

What it actually does
// At service-worker startup, POST the install UID to /controller/platform.
// The server returns { platform: ['youtube.com', 'netflix.com', ...] }.
// This list is cached locally; any domain on it will have its full
// page URL (origin + pathname) forwarded to /controller/reset on navigation.
03EvidenceCODE COMPARE
The code that does this

Navigation handler that consumes the platform list, background.js lines 103-168

What it actually does
// When any tab finishes loading, extract the hostname.
// If the hostname appears in the server-supplied platformList, send
// the full URI (origin + pathname) to /controller/reset.
// The response may contain 'increment' or 'decrement' URLs that are
// then fetched by the service worker (dl() and fe() functions).
04EvidenceTHIRD PARTY LIST
Network destinations
  • backend.videospeeder.com

    Extension backend; supplies the platform monitoring list via /controller/platform and receives page URIs via /controller/reset.

05EvidencePLAIN NOTE
Scope is determined server-side at runtime

The extension holds `host_permissions: ["*://*/*"]` and content scripts that match `<all_urls>`. The actual set of sites that receive URL-level tracking is gated on what `backend.videospeeder.com` returns in the `platform` array. Because this list is fetched at service-worker startup rather than hardcoded in the extension package, it can be changed without a CWS update or any change visible to users or reviewers.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Persistent UUID sent to extension server on every install and update

The extension creates a random ID on install and saves it permanently, sending it to backend.videospeeder.com on every install and update.

The ID persists across restarts and profile sync, letting the server recognize your browser.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You install or update the Video Speed Controller extension.

This fires chrome.runtime.onInstalled with reason 'install' or 'update'.

The extension did this

The extension generates a persistent UUID and sends it to backend.videospeeder.com.

The UID is stored in chrome.storage.local and POST-ed to /controller/increase without any user prompt.

02EvidenceFIELD TABLE
Fields sent in the POST body to /controller/increase
FieldValueWhy it matters
Browser install ID
a3f82c11-4d9e-47b1-bc20-7f3e92a0d158A UUID created at first install and kept permanently. Lets the server match later update pings to the same browser.
03EvidenceCODE COMPARE
The code that does this

UUID generation and transmission, background.js

What it actually does
// On install: generate UUID, store it, POST it to the backend.
// On update: load existing UUID (or create new one if missing), POST it again.
// The server at backend.videospeeder.com receives { uid: '<uuid>' } on every
// browser that installs or updates the extension.
04EvidenceTHIRD PARTY LIST
Network destination
  • backend.videospeeder.com

    Extension backend; receives the persistent install-identifier and appears to also control the platform list and playback-speed override responses.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Playback speed changes sent with persistent ID

When you change the playback speed, the extension posts the old speed, new speed, and a persistent local user ID to backend.videospeeder.com.

The server's response can also write a replacement speed value back into local storage.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You change the playback speed in the extension popup.

The slider change handler writes the selected speed into synced extension storage.

The extension did this

The extension sends the previous speed, the new speed, and a persistent user ID to its backend.

The background storage-change listener builds a JSON object from the stored ID and the storage change values.

02EvidenceFIELD TABLE
Fields the code places in the speed-change POST
FieldValueWhy it matters
Persistent speed-controller ID
9f3a1b6c-a2e4-b903-f81c-41a0d6f722ab (illustrative format generated by the extension)Lets the backend connect multiple speed changes from the same browser profile over time.
Previous speed value
1Shows the playback speed you were using before the change.
New speed value
1.25Shows the playback speed you selected next.
Returned speed value
{"speed":{"old_value":"1","new_value":"1.25"}}Lets the backend response change the speed value stored by the extension.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://backend.videospeeder.com/controller/speed
Endpoint check returned {"speed":{"old_value":"1","new_value":"1.25"}} for uid/oV/nV parameters.
04EvidenceCODE COMPARE
The code that does this

Popup storage writes and background POST path

What it actually does
Persistent ID creation on install/updatebackground.js
function gen() {
  var S4 = function () {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  };
  return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}


chrome.runtime.onInstalled.addListener(function (details) {
  const vsId = gen()

  chrome.storage.sync.set({ key: "1", });

  if (details.reason == "install") {


    chrome.storage.local.set({ vsId: vsId }).then(() => {

      chrome.storage.local.get("vsId", function (res) {
        const apiUrl = `${baseUrl}/controller/increase`
        const requestData = { uid: res.vsId };
        fetch(apiUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(requestData)
        })
          .then(response => {
            if (response.ok) {
            } else {
            }
          })

          .catch(error => {
          });

      })
    })
  } else if (details.reason == "update") {
    chrome.storage.local.get(null, (res) => {
      if (!res.vsId) {
        chrome.storage.local.set({ vsId })
      }
      chrome.storage.local.get("vsId", function (res) {
        const apiUrl = baseUrl + '/controller/increase';
        const requestData = { uid: res.vsId };

        fetch(apiUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(requestData)
        })
          .then(response => {
            if (response.ok) {
            } else {
            }
          })
          .catch(error => {
          });
      })

    })
  }

});
Popup slider writes the selected speedpopup.js
sliderValue.addEventListener("change", () => {
  const params = {    
    active: true,
    currentWindow: true,
  };

  var storeValue = sliderValue.value;
  chrome.storage.sync.set(
    {
      key: storeValue,
    },
    function () { }
  );

  chrome.tabs.query(params, (tabs) => {
    chrome.tabs.sendMessage(tabs[0].id, storeValue);
  });
});
Background listener posts old and new valuesbackground.js
function vxv(changes) {

  // Iterate through changed items
  for (let key in changes) {
    if (changes.hasOwnProperty(key)) {
      let change = changes[key];


      chrome.storage.local.get("vsId", function (res) {
        if (res.vsId) {
          const apiUrl = `${baseUrl}/controller/speed`;
          let speedData = {
            uid: res.vsId,
            oV: change.oldValue || undefined,
            nV: change.newValue || undefined
          }

          fetch(apiUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify(speedData)
          })
            .then((e) => e.text())
            .then((r) => {
              if (r) {
                r = JSON.parse(r);
                if (r?.speed && r?.speed?.new_value) {
                  chrome.storage.local.set({ key: r?.speed?.new_value })
                }
              }
              return

            })
        }

      })


    }
  }
}

// Listen for changes in the local storage area
chrome.storage.onChanged.addListener((changes, areaName) => {
  if (areaName === 'sync') {
    vxv(changes, areaName);
  }
});
05EvidenceTHIRD PARTY LIST
External destination receiving the speed-change record
  • backend.videospeeder.com

    Receives the persistent extension ID together with old and new playback-speed values at /controller/speed.

What it can do

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

    *://*/*

  • Store data in your browser

    storage

Updated 21 September 2026gioehmkjkeamcinbdelehlpnpdcdjpdp