Security Alert: Critical Security Risk
Dictionary
ID: nhbchcfeodkcblfpdjdhelcfbefefmag
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- e-dictionary.orgView Profile
- Privacy
- Privacy Policy
- MX records exist
- No
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- No
- Website
- Visit
Full-featured dictionary extension
Easily look up word definitions, pronunciations and synonyms as you browse the web
The extension silently strips the Content-Security-Policy response header from every website the user visits (both top-level pages and all sub-frames) using a declarativeNetRequest rule. CSP is the primary browser mechanism that prevents cross-site scripting and data injection attacks; removing it globally across all sites means every website the user browses is left unprotected. A legitimate dictionary extension has no need to modify security headers on arbitrary third-party websites.
[ { "id": 1, "priority": 1, "action": { "type": "modifyHeaders", "responseHeaders": [ { "header": "content-security-policy", "operation": "remove" } ] }, "condition": { "urlFilter": "*://*/*", "resourceTypes": [ "main_frame", "sub_frame" ] } }]A second declarativeNetRequest rule strips the X-Frame-Options header from every sub-frame response across all websites. X-Frame-Options is a critical clickjacking defense that prevents pages from being embedded in iframes without the site's consent. Globally disabling this for all sub-frames allows any third-party page to be silently embedded in iframes inside other pages, enabling clickjacking and UI-redressing attacks on every site the user visits.
{ "id": 2, "priority": 1, "action": { "type": "modifyHeaders", "responseHeaders": [ { "header": "x-frame-options", "operation": "remove" } ] }, "condition": { "urlFilter": "*://*/*", "resourceTypes": [ "sub_frame" ] }}The extension uses the `cookies` permission to access the LSID authentication cookie from accounts.google.com to determine whether the user is currently signed in to their Google account. While the boolean result is only used locally to gate a feedback prompt, accessing a sensitive Google authentication session token is far outside the scope of a dictionary extension. This represents unauthorized collection of account state data that could be adapted for credential probing.
async isSignedInToGoogle() { return new Promise(e => { chrome.cookies.get({ url: "https://accounts.google.com", name: "LSID" }, n => { e(!!n) }) })}The extension generates a persistent random UUID as a client ID stored in local storage and transmits it along with usage events (install, uninstall, context menu clicks) to Google Analytics using a hardcoded Measurement Protocol API secret. This constitutes persistent cross-session user tracking without disclosure or consent. The permanent client ID allows the developer to correlate all of a user's extension activity over time.
var fr = "https://www.google-analytics.com/mp/collect", lr = "https://www.google-analytics.com/debug/mp/collect", dr = "G-7FSX884R4G", pr = "vWEdjAcARCOZxI6zaJ-l2g";var lt = class { constructor() { this.debug = IS_DEV_BUILD } async getOrCreateClientId() { let { clientId: e } = await chrome.storage.local.get("clientId"); return e || (e = self.crypto.randomUUID(), await chrome.storage.local.set({ clientId: e })), e } async fireEvent(e, n = {}) { n.session_id || (n.session_id = await this.getOrCreateSessionId()), n.engagement_time_msec || (n .engagement_time_msec = 100); try { let r = await fetch(`${this.debug?lr:fr}?measurement_id=${dr}&api_secret=${pr}`, { method: "POST", body: JSON.stringify({ client_id: await this.getOrCreateClientId(), events: [{ name: e, params: n }] }) });On every tab activation and tab update event, the background script queries whether the user is in a normal (non-incognito) window, checks their Google sign-in status via cookie, measures days since install, and counts interaction events. While this is framed as eligibility logic for a feedback prompt, it establishes continuous behavioral profiling of the user's browsing session on all tab navigations, which is disproportionate for a dictionary tool.
async shouldRequestFeedback(e) { let n = !await this.isIncognito(e), r = await this.isSignedInToGoogle(), o = await this.getDaysSinceInstallation() > 7, i = await this.getSuccessCount() >= 30, s = await this.isEligibleForReissue(), a = n && r && o && i && s; return this.logger.debug(`isEligible: ${a}. Based on isNormalWindow: ${n}, isSignedIn: ${r}, isAgedInstallation: ${o}, hasSufficientSuccessfulInteractions: ${i}, isEligibleForReissue: ${s}`), a}By severity
Versions scanned
Showing 1 of 1 scanned version with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 23.11.11 | 5 |
Files with findings
2 distinct paths — top paths by unique finding count:
- background-script/background.js3
- assets/rules.json2
URLs
View the external URLs this extension communicates with to understand its network activity and data interactions.
Gain full insight into all external connections.
Upgrade for full visibility.
Gain full insight into all external connections.
Upgrade for full visibility.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.