Security Alert: Confirmed Malware
EditThisCookie
ID: fngmhnnpilhplaeedifhccceomclgfbg
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- https://editthiscookie.comView Profile
- Help
- Help Center
- Country
- GB
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- Yes
- Mailbox exists
- Yes
- Address
- Ridgdale Street London E32TW GB
- Website
- Visit
EditThisCookie is a cookie manager. You can add, delete, edit, search, protect and block cookies!
The first and best cookie manager for Google Chrome. โ Edit, delete, create cookies โ Make them read-only โ Block them (create filters) โ Export to JSON, Netscape cookie file (perfect for wget and curl), Perl::LPW โ Import from JSON โ Limit the maximum expiration date of any cookie โ Improve the performance, remove old ones โ Import a cookies.txt file
A blocking `webRequest.onHeadersReceived` listener intercepts ALL HTTP responses across all URLs and reads raw `Set-Cookie` header values โ including cookie names, values, and domains โ before the browser processes them. The listener is registered with `blocking`, `responseHeaders`, and `extraHeaders` flags on `<all_urls>`, giving the extension visibility into every cookie being set by every website the user visits. Combined with `webRequestBlocking`, it can silently suppress any cookie from any response without user notification.
chrome.webRequest.onHeadersReceived.addListener( function(details) { if (details.responseHeaders !== undefined) { headersToForward = []; headersChanged = false; for (var i = 0; i < details.responseHeaders.length; i++) { cH = details.responseHeaders[i]; if (cH.name.toUpperCase() == "SET-COOKIE") { fields = cH.value.split(';'); var cookieName = undefined; var cookieDomain = undefined; var cookieValue = undefined; if (fields.length > 0) { cookieName = fields[0].split('=')[0] cookieValue = fields[0].split('=')[1] } for (var x = 1; x < fields.length; x++) { if (fields[x].split('=')[0].trim() == "domain") { cookieDomain = fields[x].split('=')[1]; break; } }The `webRequest` listener is registered with `blocking` + `extraHeaders` on `<all_urls>`, granting access to otherwise-protected headers including `Set-Cookie` for every HTTP/HTTPS request in the browser. The `extraHeaders` flag specifically bypasses Chrome's default header protection. This allows the extension to read and strip cookies from responses for any site.
{ urls: ["<all_urls>"]},["blocking", "responseHeaders", "extraHeaders"]);The extension dynamically injects an external script from `https://ssl.google-analytics.com/ga.js` into the popup page at runtime using `document.createElement('script')`. This constitutes remote code loading โ any compromise or modification of the hosted `ga.js` would execute arbitrary JavaScript in the extension popup context, which has full `chrome.cookies` access over all domains for 2 million users. There is no subresource integrity check.
var _gaq = _gaq || [];_gaq.push(['_setAccount', 'UA-33054271-5']);_gaq.push(['_setSessionCookieTimeout', 0]);_gaq.push(['_trackPageview']);(function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = 'https://ssl.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);})();setInterval(function() { _gaq.push(['_trackEvent', 'Heartbeat', 'Heartbeat']);}, 4 * 60 * 1000);The popup HTML loads `ga.js` which dynamically fetches and executes a remote Google Analytics script in the same execution context as the popup, which has access to `chrome.cookies`, `<all_urls>`, and all other privileged extension APIs. Any attacker who could modify or substitute the remote GA endpoint would gain full access to all browser cookies across all sites via the extension's permissions. This is a critical privilege escalation path through a remote code loading vector.
<script type="text/javascript" src="js/ga.js"></script><script type="text/javascript" src="/js/cookie_helpers.js"></script><script type="text/javascript" src="/js/utils.js"></script><script type="text/javascript" src="/js/data.js"></script><script type="text/javascript" src="js/popup.js"></script>The background devtools page retrieves all cookies for an inspected tab URL and relays the full cookie array โ including session tokens, HttpOnly cookies, and secure cookies โ via a port message to the devtools panel script. The full set of cookie credentials for any inspected tab is collected in bulk and passed to a secondary execution context, creating a high-risk aggregation point for credential data.
function getAll(port, message) { chrome.tabs.get(message.tabId, function(tab) { var url = tab.url; console.log("Looking for cookies on: " + url); chrome.cookies.getAll({ url: url }, function(cks) { console.log("I have " + cks.length + " cookies"); port.postMessage({ action: "getall", url: url, cks: cks }); }); });}The background page accepts arbitrary cookie modification commands from the devtools panel via message passing โ deleting existing cookies and setting new ones for any URL โ with no authentication or origin check beyond the port connection. If the devtools panel were compromised (e.g., via XSS in panel-rendered content), an attacker could overwrite session cookies for any site, enabling session fixation or cookie injection attacks.
} else if (action === "submitCookie") { var cookie = message.cookie; var origName = message.origName; deleteCookie(cookie.url, origName, cookie.storeId); chrome.cookies.set(cookie); issueRefresh(port);}The function calls `chrome.cookies.getAll({})` with an empty filter, which retrieves ALL cookies across ALL domains in the browser, then iterates and modifies every cookie's expiration date. This is the broadest possible cookie enumeration โ a complete snapshot of every session token, authentication credential, and tracking cookie across all sites โ obtained and processed in a single operation.
chrome.cookies.getAll({}, function(cookies) { totalCookies = cookies.length; cookiesShortened = 0; $("span", "#shortenProgress").text("0 / " + totalCookies); shortenCookies(cookies, setOptions);});function shortenCookies(cookies, callback) { if (cookies.length <= 0) { data.nCookiesShortened += cookiesShortened; $("#shortenProgress").fadeOut(function() { if (callback != undefined) callback(); }); return; } $("span", "#shortenProgress").text((totalCookies - cookies.length) + " / " + totalCookies); var cookie = cookies.pop(); var maxAllowedExpiration = Math.round((new Date).getTime() / 1000) + (preferences.maxCookieAge * preferences.maxCookieAgeType); if (cookie.expirationDate != undefined && cookie.expirationDate > maxAllowedExpiration) { var newCookie = cookieForCreationFromFullCookie(cookie); if (!cookie.session) newCookie.expirationDate = maxAllowedExpiration; chrome.cookies.set(newCookie, function() { shortenCookies(cookies, callback) }); cookiesShortened++; } else shortenCookies(cookies, callback);}The `chrome.cookies.onChanged` listener captures the name, domain, and value of every cookie change event across all domains in real time. The extension has unrestricted access to all cookie values โ including session tokens and authentication cookies โ for every website the user visits. This broad surveillance of all browser cookie activity is a substantial unauthorized data collection surface for 2 million users.
chrome.cookies.onChanged.addListener(function(changeInfo) { var removed = changeInfo.removed; var cookie = changeInfo.cookie; var cause = changeInfo.cause; var name = cookie.name; var domain = cookie.domain; var value = cookie.value; if (cause === "expired" || cause === "evicted") return; for (var i = 0; i < data.readOnly.length; i++) { var currentRORule = data.readOnly[i]; if (compareCookies(cookie, currentRORule)) { if (removed) { chrome.cookies.get({ 'url': "http" + ((currentRORule.secure) ? "s" : "") + "://" + currentRORule.domain + currentRORule.path, 'name': currentRORule.name, 'storeId': currentRORule.storeId }, function(currentCookie) {The extension silently tracks every popup open as a page view and fires a heartbeat telemetry event every 4 minutes to Google Analytics account `UA-33054271-5`, allowing the extension operator to monitor the precise usage patterns of 2 million users. No consent mechanism or disclosure is present, and the tracking occurs in a privileged context alongside sensitive cookie data.
var _gaq = _gaq || [];_gaq.push(['_setAccount', 'UA-33054271-5']);_gaq.push(['_setSessionCookieTimeout', 0]);_gaq.push(['_trackPageview']);setInterval(function() { _gaq.push(['_trackEvent', 'Heartbeat', 'Heartbeat']);}, 4 * 60 * 1000);The `importCookies` function accepts arbitrary JSON text pasted by the user and directly calls `chrome.cookies.set()` on each parsed cookie object without domain validation or sanitization. A crafted JSON payload could inject cookies for any domain, potentially enabling session fixation attacks against third-party sites or overwriting security-sensitive cookies (e.g., `__Host-` prefixed cookies, SameSite attributes).
function importCookies() { var nCookiesImportedThisTime = 0; var text = $(".value", "#pasteCookie").val(); try { var cookieArray = $.parseJSON(text); if (Object.prototype.toString.apply(cookieArray) === "[object Object]") cookieArray = [cookieArray]; for (var i = 0; i < cookieArray.length; i++) { try { var cJSON = cookieArray[i]; var cookie = cookieForCreationFromFullCookie(cJSON); chrome.cookies.set(cookie); nCookiesImportedThisTime++; } catch (e) {The `element` variable is set directly from the `page` URL query parameter or from `localStorage` without any whitelist validation before being concatenated into `location.href`. An attacker who can control the URL parameter could redirect to unintended paths within the extension's origin, potentially loading attacker-influenced content in the privileged extension context.
var panel = JSON.parse(localStorage.getItem("option_panel"));var arguments = getUrlVars();var element;if (panel === "null" || panel === null || panel === undefined) { element = "support";} else { element = panel;}if (arguments.page !== undefined) { element = arguments.page;}location.href = "/options_pages/" + element + ".html";The `localizePage` function inserts translated strings via jQuery's `.html()` setter โ equivalent to `innerHTML` assignment โ without sanitization. If a locale message file were tampered with (the extension bundles 40+ locale files), translated content would be injected unsanitized into the DOM of privileged extension pages, creating a stored XSS vector that executes in the extension's elevated permission context.
function localizePage() { $('[i18n]:not(.i18n-replaced)').each(function() { $(this).html($(this).html() + translate($(this).attr('i18n'), $(this).attr('i18n_argument'))); $(this).addClass('i18n-replaced'); }); $('[i18n_value]:not(.i18n-replaced)').each(function() { $(this).val(translate($(this).attr('i18n_value'))); $(this).addClass('i18n-replaced'); }); $('[i18n_title]:not(.i18n-replaced)').each(function() { $(this).attr('title', translate($(this).attr('i18n_title'))); $(this).addClass('i18n-replaced'); }); $('[i18n_placeholder]:not(.i18n-replaced)').each(function() { $(this).attr('placeholder', translate($(this).attr('i18n_placeholder')) + $(this).attr('placeholder')); $(this).addClass('i18n-replaced'); });User-supplied filter rule strings (domain, name, value) are passed directly into `new RegExp()` without sanitization or escaping. This function is called inside the blocking `webRequest` handler, so a catastrophically backtracking regex (ReDoS payload) injected via a filter rule could freeze the browser's network stack โ effectively a denial-of-service against all network activity.
function filterMatchesCookie(rule, name, domain, value) { var ruleDomainReg = new RegExp(rule.domain); var ruleNameReg = new RegExp(rule.name); var ruleValueReg = new RegExp(rule.value); if (rule.domain !== undefined && domain.match(ruleDomainReg) === null) { return false; } if (rule.name !== undefined && name.match(ruleNameReg) === null) { return false; } if (rule.value !== undefined && value.match(ruleValueReg) === null) { return false; } return true;}The `cookiesToString` functions serialize full cookie objects โ including name, value, domain, path, secure flag, httpOnly flag, and expiration โ into clipboard-ready strings. The variable `cookie` is assigned without `var`/`let`/`const` (implicit global). Full serialization of all cookie attributes including sensitive session token values into a copyable string represents credential exposure, particularly if this function can be triggered without sufficiently deliberate user action.
"json": function(cookies, url) { var string = ""; string += "[\n"; for (var i = 0; i < cookies.length; i++) { cookie = cookies[i]; cookie.id = i + 1; string += JSON.stringify(cookie, null, 4); if (i < cookies.length - 1) string += ",\n"; } string += "\n]"; return string;},By severity
Versions scanned
Showing 4 of 6 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 1.6.2 | 4 |
| 1.6.1 | 17 |
| 1.6 | 14 |
| 1.5.0 | 3 |
Files with findings
14 distinct paths โ top paths by unique finding count:
- js/ga.js9
- js/background.js8
- js/utils.js3
- devtools/background-devtools.js2
- devtools/panel.js2
- js/cookie_helpers.js2
- js/options_main_page.js2
- lib/i18n_translator.js2
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.
Code Diff
Compare extension code between any two versions.
No comparable text files found between these versions.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.