Is Uninquizitive safe?
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.
Who publishes itUIQ - no other listings under this identity
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
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 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.
- Current gradecurrent_grade = 100
This is the grade value shown by the coursework page, so changing it changes what you see as completed work.
- Submitted gradesubmitted_grade = 100
This is the value the page is prepared to report through its own grade-submission flow.
- Question outcomeadjust_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 termsdetect, telemetry, extension, automation, check_question
Requests with these terms can receive local success-like responses instead of normal page handling.
Runner control reaches MAIN-world scoring functions
Readable equivalent of the runner and scoring handlers
static/js/a.js and static/js/b.jsfunction 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; } }});Page shield removes warning dialogs and intercepts matching requests
Readable equivalent of the shield logic
static/js/ep.jsconst 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.