Is Uninquizitive safe?

Medium risk

Uninquizitive modifies Norton InQuizitive pages to change scoring state and suppress anti-automation checks.

On scoped W. W. Norton InQuizitive pages, Uninquizitive runs page-level scripts that call scoring functions, set grade values, and hide or block anti-automation dialogs and detection traffic. When those detection events occur, it sends the page URL plus DOM, network, tab, and local storage context to www.uninquizitive.com.

45Risk
Who publishes it

UIQ - no other listings under this identity

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

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

Publishers can request a review.

Findings

Norton InQuizitive score and detection handling changes

Source code shows Uninquizitive runs only on W.W.

Norton and InQuizitive pages, using page-world code to change score/grade state and call grade-submission functions.

It also removes warning dialogs and fakes success on detection calls.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-506
Source
Dynamic sandbox
What actually happens
You did this

You use the extension on a supported W.W. Norton or InQuizitive coursework page.

The manifest limits these content scripts to Norton coursework origins and the extension popup/runner sends commands such as RUN_TOPOFF.

The extension did this

The extension runs code inside the page to change scoring state and alter detection handling.

The service worker executes MAIN-world functions that touch page scoring globals, while the page shield removes matching dialogs and returns success-like responses for matching requests.

Concrete page fields and checks touched by the code
  • Current grade
    current_grade = 100

    This is the grade value shown by the coursework page, so changing it changes what you see as completed work.

  • Submitted grade
    submitted_grade = 100

    This is the value the page is prepared to report through its own grade-submission flow.

  • Question outcome
    adjust_score("correct")

    The extension can mark the current question outcome as correct before moving to the next question.

  • Warning dialogs
    #cmodalOverlay,.c-modal-overlay,#cmodal,.cmodal

    The extension removes dialogs that match Norton-style extension or automation warning patterns.

  • Detection request terms
    detect, telemetry, extension, automation, check_question

    Requests with these terms can receive local success-like responses instead of normal page handling.

The code that does this

Runner control reaches MAIN-world scoring functions

Readable version

Readable equivalent of the runner and scoring handlers

static/js/a.js and static/js/b.js
function requestTopoff(options = {}) {  try {    log("runner", "requestTopoff", options);    chrome.runtime?.sendMessage?.(Object.assign({ type: "RUN_TOPOFF" }, options), function () {});  } catch (error) {    log("runner", "requestTopoff error", error?.message || error);  }}chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {  switch (message.type) {    case "RUN_TOPOFF": {      try {        const tabId = sender.tab?.id;        const frameId = sender.frameId ?? 0;        if (!tabId) return sendResponse(errorResponse(new Error("tabId missing")));        chrome.scripting.executeScript({          target: { tabId, frameIds: [frameId] },          world: "MAIN",          func: function (args) {            const secs = Number(args && args.secs) || 0;            function runLoop(score, index) {              if (index < 3) {                try {                  if (typeof score.set_bet === "function") score.set_bet("20");                  else score.set_bet = "20";                } catch (error) {}                try {                  score.adjust_score?.call(score, "correct");                } catch (error) {}                setTimeout(function () {                  try {                    score.adjust_score?.call(score, "correct");                  } catch (error) {}                  setTimeout(function () {                    try {                      if (window.FAQ && typeof window.FAQ.next_question === "function") {                        window.FAQ.next_question();                      } else {                        const next = Array.from(document.querySelectorAll("button,a")).filter(function (el) {                          return /next question/i.test((el.textContent || "").trim()) && !el.disabled;                        })[0];                        if (next) next.click();                      }                    } catch (error) {}                    setTimeout(function () { runLoop(score, index + 1); }, 140);                  }, 80);                }, 60);                return;              }              try {                score.current_grade = 100;                score.submitted_grade = 100;                score.grade_reported = true;              } catch (error) {}              if (secs > 0) {                try { score.total_time_taken = secs; } catch (error) {}              }              try { if (typeof score.compute_grade === "function") score.compute_grade(); } catch (error) {}              try { if (typeof score.write_score === "function") score.write_score(); } catch (error) {}              try { if (typeof score.submit_grade === "function") score.submit_grade(); } catch (error) {}            }            function waitForScore(tries) {              try {                const score = window.Score;                if (score && typeof score.adjust_score === "function") runLoop(score, 0);                else if (tries < 80) setTimeout(function () { waitForScore(tries + 1); }, 50);              } catch (error) {}            }            waitForScore(0);          },          args: [{ secs: Number(message.secs) || 0 }],        }, function () {          if (chrome.runtime.lastError) return sendResponse(errorResponse(new Error(chrome.runtime.lastError.message)));          sendResponse({ success: true });        });      } catch (error) {        sendResponse(errorResponse(error));      }      return true;    }    case "IQH_SET_BET": {      try {        const tabId = sender.tab?.id;        const frameId = message.frameId ?? sender.frameId ?? 0;        if (!tabId) return sendResponse(errorResponse(new Error("tabId missing")));        chrome.scripting.executeScript({          target: { tabId, frameIds: [frameId] },          world: "MAIN",          func: function (value) {            try {              if (typeof window.Score?.set_bet !== "function" && window.Score) window.Score.set_bet = Number(value);            } catch (error) {}          },          args: [message.value],        }, function () {          if (chrome.runtime.lastError) return sendResponse(errorResponse(new Error(chrome.runtime.lastError.message)));          sendResponse({ success: true });        });      } catch (error) {        sendResponse(errorResponse(error));      }      return true;    }    case "IQH_ADJUST": {      try {        const tabId = sender.tab?.id;        const frameId = message.frameId ?? sender.frameId ?? 0;        if (!tabId) return sendResponse(errorResponse(new Error("tabId missing")));        chrome.scripting.executeScript({          target: { tabId, frameIds: [frameId] },          world: "MAIN",          func: function (outcome) {            try { window.Score?.adjust_score?.call(window.Score, outcome); } catch (error) {}          },          args: [message.outcome],        }, function () {          if (chrome.runtime.lastError) return sendResponse(errorResponse(new Error(chrome.runtime.lastError.message)));          sendResponse({ success: true });        });      } catch (error) {        sendResponse(errorResponse(error));      }      return true;    }  }});
The code that does this

Page shield removes warning dialogs and intercepts matching requests

Readable version

Readable equivalent of the shield logic

static/js/ep.js
const ownModalSelector = [  '[data-iqh-modal="true"]',  '[data-iqh-root]',  '.iqh-modal',  document.documentElement?.getAttribute('data-iqh-our-attr') ? '[' + document.documentElement.getAttribute('data-iqh-our-attr') + '="1"]' : null,].filter(Boolean).join(',');const nortonModalSelector = '#cmodalOverlay,.c-modal-overlay,#cmodal,.cmodal,.ui-widget-overlay,.ui-dialog:not(.iqh-modal)';const ariaModalSelector = '[role="dialog"][aria-modal="true"]';function isDetectionDialog(node) {  try {    if (!node) return false;    if (node.matches?.(ownModalSelector)) return false;    if (node.closest?.('.iqh-modal')) return false;    if (node.matches?.(nortonModalSelector)) return true;    if (node.matches?.(ariaModalSelector)) {      const text = (node.textContent || '').toLowerCase();      if (text.includes('important action required') || text.includes('unauthorized browser extension')) return true;    }    const rect = node.getBoundingClientRect();    const style = getComputedStyle(node);    return style.position === 'fixed' && rect.width >= 0.8 * innerWidth && rect.height >= 0.6 * innerHeight && (parseInt(style.zIndex || '0', 10) || 0) >= 9999;  } catch (error) {    return false;  }}function removeDetectionDialogs(root) {  if (window.__IQH_ENV_DISABLED) return;  try {    root.querySelectorAll?.(nortonModalSelector).forEach(function (node) {      try {        if (!node.matches(ownModalSelector)) {          node.remove();          recordShieldEvent('rm-overlay', { selector: 'overlay', id: node.id || '', class: node.className || '' });        }      } catch (error) {}    });    root.querySelectorAll(ariaModalSelector).forEach(function (node) {      try {        if (isDetectionDialog(node)) {          node.remove();          recordShieldEvent('rm-dialog', { selector: 'dialog', id: node.id || '', class: node.className || '' });        }      } catch (error) {}    });  } catch (error) {}}function shouldBlockRequest(url, body) {  try {    if (!config.blockOn || isAllowedByLocalConfig(url)) return false;    const lowerUrl = (url || '').toLowerCase();    const lowerBody = (typeof body === 'string' ? body : '').toLowerCase();    let score = 0;    if (['unauthor', 'detect', 'security', 'telemetry'].some((term) => lowerUrl.includes(term) || lowerBody.includes(term))) score += 2;    if (['extension', 'plugin', 'automation', 'modal', 'cmodal', 'check_question', 'report', 'flag'].some((term) => lowerUrl.includes(term) || lowerBody.includes(term))) score += 1;    if (['google-analytics', 'gtag', 'segment', 'mixpanel', 'braze', 'appboy', 'datadog', 'newrelic', 'rudderstack', 'heap', 'hotjar', 'sentry'].some((term) => lowerUrl.includes(term))) score += 1;    if (/\bext(ension)?\b/.test(lowerUrl)) score += 1;    if (/\b(telemetry|detect|report|collect)\b/.test(lowerUrl)) score += 1;    try {      const parsed = new URL(url, location.href);      if (parsed.hostname.endsWith('wwnorton.com') && /(detect|report|telemetry|check_question)/.test(parsed.pathname)) score += 1;    } catch (error) {}    return score >= config.threshold;  } catch (error) {    return false;  }}const originalFetch = window.fetch;window.fetch = function (input, init) {  const url = typeof input === 'string' ? input : input?.url || '';  const body = typeof (init || (input?.url ? null : input))?.body === 'string' ? (init || input).body : '';  if (shouldBlockRequest(url, body)) {    recordShieldEvent('block-fetch', { url });    return Promise.resolve(new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } }));  }  return originalFetch.apply(this, arguments);};const originalOpen = XMLHttpRequest.prototype.open;const originalSend = XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open = function (method, url) {  this.__iqhU = url || '';  return originalOpen.apply(this, arguments);};XMLHttpRequest.prototype.send = function (body) {  if (shouldBlockRequest(this.__iqhU || '', typeof body === 'string' ? body : '')) {    recordShieldEvent('block-xhr', { url: this.__iqhU || '' });    this.readyState = 4;    this.status = 200;    this.responseText = '{}';    this.response = this.responseText;    this.onload?.();    this.onreadystatechange?.();    return;  }  return originalSend.apply(this, arguments);};const originalBeacon = navigator.sendBeacon;if (originalBeacon) {  navigator.sendBeacon = function (url, body) {    if (shouldBlockRequest(url, typeof body === 'string' ? body : '')) {      recordShieldEvent('block-beacon', { url });      return true;    }    return originalBeacon.apply(this, arguments);  };}const OriginalWebSocket = window.WebSocket;window.WebSocket = function (url, protocols) {  if (shouldBlockRequest(url, '')) {    recordShieldEvent('block-ws', { url });    throw Error('WS blocked');  }  return new OriginalWebSocket(url, protocols);};

What it can do

Permissions this extension asks for, as declared in version 3.7.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 4.1.0, which we have not unpacked yet.

  • Read and change your data on wwnorton.com

    https://*.wwnorton.com/*

  • Read and change your data on www.uninquizitive.com

    https://www.uninquizitive.com/*

  • Read and change your data on uninquizitive.com

    https://uninquizitive.com/*

  • Store data in your browser

    storage

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

    tabs

  • Keep your screen or computer awake

    power

  • Run its own code inside the pages you visit

    scripting

Where it sends data

Destinations our analysis observed Uninquizitive 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.

  • www.uninquizitive.com

    Uninquizitive sends data to www.uninquizitive.com. No other extension we have analysed sends data here.

Updated 30 September 2026pbiohgkbfbdlebocejhbajambbokobmn