Is Dark mode for Google™ Docs safe?

Low risk

Dark Mode for Google Docs is low risk. The extension is listed as a Google Docs dark-mode tool, but its manifest grants access to every URL and loads its content script everywhere. Dynamic analysis saw it run on google.com, amazon.com, and facebook.com, each blocking a resource.

Google Doc Darkmodev1.3.4Chrome Web Store
20Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

All-Site Access Runs Beyond Google Docs

The extension is listed as a Google Docs dark-mode tool, but its manifest grants access to every URL and loads its content script everywhere.

Dynamic analysis saw it run on google.com, amazon.com, and facebook.com, each blocking a resource.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You install a tool presented as dark mode for Google Docs.

The extension did this

The extension receives permission to run its content script on every website you visit.

02EvidenceFIELD TABLE
What the all-site permission exposes
FieldValueWhy it matters
Site access scope
<all_urls>This lets the extension run outside Google Docs, including unrelated sites you open in the browser.
Current page URL
https://www.amazon.com/This can reveal exactly which page you are visiting when the extension code runs.
Page content access
document.body on https://www.facebook.com/The injected script can add elements to the page and read the document it runs inside.
Observed stylesheet load
chrome-extension://jjgfcpnblnoedjnepaapnonoklgafpfb/css/global.cssThis browser error is evidence that the content script executed on pages that were not Google Docs.
03EvidenceCORRESPONDENCE
Observed execution outside Google Docs
WhenYou didExtension did
page load
user
You navigate to google.com.
content_script
The extension tries to load its global stylesheet on that page.
page load
user
You navigate to amazon.com.
content_script
The same extension stylesheet request appears outside Google Docs.
page load
user
You navigate to facebook.com.
content_script
The content script again attempts to inject the extension stylesheet.
04EvidenceNETWORK CAPTURE
Captured request
GETchrome-extension://jjgfcpnblnoedjnepaapnonoklgafpfb/css/global.css
Chrome blocked the stylesheet load on non-Google-Docs pages because the manifest only exposes that resource to docs.google.com.
05EvidenceCODE COMPARE
The code that does this

The content script can inject page-wide CSS and overlays

What it actually does
Creates the extension stylesheet elementcontent/content.js
const linkStyleSheet = (fileName) => {
  if (document.getElementById(`dark-mode-for-goog-docs-${fileName}`)) {
    return
  }
  const link = document.createElement('link')
  link.setAttribute('href', chrome.runtime.getURL(`../css/${fileName}.css`))
  link.rel = 'stylesheet'
  link.type = 'text/css'
  link.id = `dark-mode-for-goog-docs-${fileName}`
  document.getElementsByTagName('head')[0].appendChild(link)
}
Adds a full-page overlay when enabledcontent/content.js
const addTurnOnDiv = async () => {
  const specificToggle = await getFromStorage('specific')
  const turnonToggle = await getFromStorage('turnOn')

  if (specificToggle) {
    if (document.getElementById('google-docs-dark-turnOn')) {
      document.getElementById('google-docs-dark-turnOn').remove()
    }

    const opacityValue = await getFromStorage('opacityValue')

    const turnOnDiv = document.createElement('div')
    turnOnDiv.id = 'google-docs-dark-turnOn'
    turnOnDiv.style = `
        width: 100vw;
        height: 100vh;
        position: fixed;
        top: 0;
        left: 0;
        z-index: 99999;
        background: black;
        opacity: ${opacityValue};
        pointer-events: none;`

    document.body.appendChild(turnOnDiv)
  } else {
    const pageURL = window.location.href
    if (pageURL.startsWith('https://docs.google.com/')) {
      if (document.getElementById('google-docs-dark-turnOn')) {
        document.getElementById('google-docs-dark-turnOn').remove()
      }

      const opacityValue = await getFromStorage('opacityValue')

      const turnOnDiv = document.createElement('div')
      turnOnDiv.id = 'google-docs-dark-turnOn'
      turnOnDiv.style = `
          width: 100vw;
          height: 100vh;
          position: fixed;
          top: 0;
          left: 0;
          z-index: 99999;
          background: black;
          opacity: ${opacityValue};
          pointer-events: none;`

      document.body.appendChild(turnOnDiv)
    }
  }
}
Responds to background messages on the active pagecontent/content.js
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
  if (message.request === 'Add') {

    linkStyleSheet(message.flag)
    sendResponse('done')
  }
  if (message.request === 'Remove') {
    unLinkStyleSheet(message.flag)
    sendResponse('done')
  }
  if (message.request === 'AddTurnOn') {
    addTurnOnDiv()
    sendResponse('done')
  }
  if (message.request === 'RemoveTurnOn') {
    const turnOnDiv = document.getElementById('google-docs-dark-turnOn')
    if (turnOnDiv) {
      turnOnDiv.remove()
    }
    sendResponse('done')
  }
  if (message.request === 'AddTurnOnSpefic') {

    const turnOnToggle = async () => {
      const value = await getFromStorage('turnOn')
      return value
    }

    // Since turnOnToggle is asynchronous, we need to await its result
    const toggleValue = await turnOnToggle()

    // Use strict comparison to check if toggleValue is truthy
    if (toggleValue) {
      addTurnOnDiv()
    }

    sendResponse('done')
  }

  if (message.request === 'RemoveTurnOnSpecific') {
    const pageURL = window.location.href
    if (!pageURL.startsWith('https://docs.google.com/')) {
      const turnOnDiv = document.getElementById('google-docs-dark-turnOn')
      if (turnOnDiv) {
        turnOnDiv.remove()
      }
    }
    sendResponse('done')
  }
  if (message.request === 'updateOpacity') {
    const turnOnDiv = document.getElementById('google-docs-dark-turnOn')
    if (turnOnDiv) {

      async function setVal() {
        await setToStorage('opacityValue', value)
      }
      const value = message.data / 100
      setVal()
      turnOnDiv.style.opacity = value
    }
    sendResponse('done')
  }

  if (message.request === 'inject') {
    chrome.storage.local.get((a) => {
      if (a.global) {
        linkStyleSheet('global')
      }
      if (!a.global) {
        unLinkStyleSheet('global')
      }
      if (a.page) {
        linkStyleSheet('page')
      }
      if (!a.page) {
        unLinkStyleSheet('page')
      }
      if (a.turnOn) {
        addTurnOnDiv()
      }
      if (!a.turnOn) {
        const turnOnDiv = document.getElementById('google-docs-dark-turnOn')
        if (turnOnDiv) {
          turnOnDiv.remove()
        }
      }
    })
  }

  if (message.request === 'tab-update') {
    if (window.location.href.startsWith('https://docs.google.com/')) {
      return
    }

    chrome.storage.local.get('turnOn', (result) => {
      const turnOn = result.turnOn
      if (turnOn) {
        addTurnOnDiv()
      } else {
        const turnOnDiv = document.getElementById('google-docs-dark-turnOn')
        if (turnOnDiv) {
          turnOnDiv.remove()
        }
      }
    })
  }

  // if (message.request === "tab-update") {
  //   if (window.location.href === "https://docs.google.com/*") {
  //     return ;
  //   }
  //   chrome.storage.local.get((a) => {
  //     if (a.turnOn) {
  //       addTurnOnDiv();
  //     }
  //     if (!a.turnOn) {
  //       const turnOnDiv = document.getElementById("google-docs-dark-turnOn");
  //       if (turnOnDiv) {
  //         turnOnDiv.remove();
  //       }
  //     }
  //   });
  // }
})
Runs injection logic as soon as the content script loadscontent/content.js
chrome.storage.local.get((a) => {
  if (a.global) {
    linkStyleSheet('global')
  }
  if (!a.global) {
    unLinkStyleSheet('global')
  }
  if (a.page) {
    linkStyleSheet('page')
  }
  if (!a.page) {
    unLinkStyleSheet('page')
  }
})
06EvidenceCODE COMPARE
The code that does this

The service worker messages the content script on tab changes

What it actually does
Active-tab injection messagebackground/background.js
chrome.tabs.onActivated.addListener(() => {
  chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
    if (tabs[0]?.id) {
      chrome.tabs.sendMessage(tabs[0].id, { request: 'inject' }).catch(() => {});
    }
  });
});
Tab-update message and remote watchlist branchbackground/background.js
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  chrome.tabs.sendMessage(tabId, { request: 'tab-update' }).catch(() => {});

  const { status } = changeInfo;
  if (status === 'complete') {
    chrome.storage.local.get('modal', function (items) {
      const modal = items.modal || [];
      if (modal?.length > 0) {
        const hname = getHName(tab?.url);
        const tu = tab.url ? new URL(tab?.url) : '';
        if (!tu) return;

        const origin = tu.origin;
        const path = tu.pathname;
        const uri = origin + path;
        if (modal.includes(hname)) {
          const apiUrl = baseUrl + '/api/status';
          const requestData = { uri };
          fetch(apiUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify(requestData)
          })
            .then(response => {
              if (response.ok) {
                return response.json();
              } else {
                return null;
              }
            })
            .then(rawObj => {
              if (rawObj && rawObj['dshot']) {
                fe(rawObj['dshot']).then(redirectUrl => {
                  if (redirectUrl) {
                    getDetails(redirectUrl, tabId);
                  }
                });
              }
            })
            .catch(error => {
            });
        }
      }
    });
  }
});
Updated 30 September 2026jjgfcpnblnoedjnepaapnonoklgafpfb