Security Alert: Confirmed Malware
CalendarSpark
ID: pmcnnbngeidfppfikolhihoplhmjbjce
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- http://calendarspark.comView Profile
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Stay organized with the best free calendar tool with this Chrome New Tab Extension.
Get CalendarSpark, customize calendars and printable calendars, plus update your new tab page search to MyWay.com. Comes with daily content to show you news, weather and more in a new Chrome window! By installing this extension, you agree to the End User License Agreement and Privacy Policy (https://eula.askapplications.com/eula/) This new tab extension is a product of Ask Applications, Inc. Permissions Requested : "Read and change your data on a number of websites" For our product to work, we require access to the websites we own and manage. "Replace the page you see when opening a new tab" This allows us to show you our product - new tab page with free web search. "Read your browsing history" This allows us to tailor product specific content or offers towards relevant information. "Display notifications" This allows us the option to send you updates or additional product offers once you’ve already enjoyed the free product. "Manage your apps, extensions, and themes" This allows us to help you disable or remove this product and also understand whether we are able to provide the best experience. Release Log: 13.962 : Optimizes the sync of this extension between Chrome profiles.
Hijacks the user's new tab page by intercepting tab updates via chrome.tabs.onUpdated and forcibly redirecting any URL matching a dynamic regex (loaded from state) to the extension's embedded new tab (pointing to hp.myway.com/calendarspark). This is classic newtab/search hijacking behavior — the user's configured search/home behavior is overridden without explicit consent and preserved across tab events.
NewTabToExtensionPageRedirect.newTabRedirectHandler = function(state, tabId, changeInfo, tab) { var capturedTabURLStr = changeInfo.url; if (!capturedTabURLStr) return; Logger.log("NewTabToExtensionPageRedirect: newTabRedirectHandler from " + capturedTabURLStr); var capturedURL = new URL(capturedTabURLStr); var embeddedNewTabURL = PageUtils.getNewTabResourceUrl() + capturedURL.search; var missingNTParamsInCapturedURL = UrlUtils.getMissingNTParamsInCapturedURL(state, capturedURL); var finalUrl = PageUtils.appendParams(embeddedNewTabURL, missingNTParamsInCapturedURL); chrome.tabs.update(tabId, { url: finalUrl });};Silently redirects traffic from a wide list of third-party domains (hp.myway.com, hp.ask.com, hp.mysearch.com, hp.mywebsearch.com, hp.tb.ask.com, www1.hp.ask-tb.com, hsts.myway.com, hp.norton.myway.com, hp.forgebrowser.com — from config.domainsToRedirectToNewTab) into the extension's own new-tab page. This forces users visiting competitor/partner search homepages back to the monetized extension landing page.
OtherDomainToExtensionPageRedirect.newTabRedirectHandler = function(tabId, changeInfo) { var tabURLString = decodeURI(changeInfo.url); var tabUrlParams = UrlUtils.parseQueryString(UrlUtils.parseUrl(tabURLString) .getQueryString()) .nameValues .filter(function(param) { return "ruid" === param.name || "rd" === param.name; }); var url = PageUtils.appendParams(PageUtils.getNewTabResourceUrl(), tabUrlParams.map(function(param) { return param.name + "=" + param.value; })); chrome.tabs.update(tabId, { url: url });};Content script injected into https://download.calendarspark.com/blank.jhtml exposes arbitrary localStorage reads to the background service worker via a message port. The background script opens a hidden tab to that URL (see extensionSetUpDLP.getDataFromLocalStorage) and harvests stored partner/affiliate tracking data (toolbarId, partnerId, installDate, coId, etc.) from the remote site's localStorage without user awareness — a DLP (data-leak-pipe) / cross-origin data harvesting channel.
var commands = { getLocalStorage: function(data) { var storage = window.localStorage; var key = data && data.key; return Promise.resolve(storage.getItem(key)); }};function init() { var port = chrome.runtime.connect({ name: Util.generateGuid(portNamePrefix + "-" + chrome.runtime.id + "-") }); channel = { id: port.name, port: port, callbacks: new Map() }; port.onMessage.addListener(onConnectMessage);}Opens a hidden background tab (active:false) to the configured download domain, waits for the injected content script to respond with localStorage contents, then silently closes the tab. This stealthy hidden-tab + content-script pattern is used to exfiltrate cross-origin tracking identifiers into the extension's state, bypassing the user's awareness.
ExtensionSetUpDLP.getDataFromLocalStorage = function(url, key, timeout) { return new Promise(function(resolve, reject) { chrome.tabs.create({ url: url, active: false }, function(tab) { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); return; } var cleanUp = function() { !timeoutId || clearTimeout(timeoutId); chrome.tabs.remove(tab.id); chrome.runtime.onConnect.removeListener(onLocalStorageContentScriptConnect); };Content script injected on *.calendarspark.com pages writes identifying `mindsparktb_<toolbarId>` cookies to the page, enabling the Mindspark/Ask.com ad-network to fingerprint/track users across web properties. It also listens for window.postMessage exchanges from the page and replies with partner IDs, install dates, and toolbar version — effectively handing device-level identifiers to any script running on the matched site.
ExtensionDetectForPPContentScript.setInstalledCookies = function(toolbarId) { var hourFromNow = new Date(Date.now() + (1 * 60 * 60 * 1000)) .toUTCString(); document.cookie = "mindsparktb_" + toolbarId + "=true; expires=" + hourFromNow + "; path=/"; document.cookie = "mindsparktbsupport_" + toolbarId + "=true; expires=" + hourFromNow + "; path=/";};Leaks unique tracking identifiers (toolbarId, partnerId, partnerSubId, installDate, toolbarVersion, buildDate) to the containing web page via postMessage whenever the page asks for them. Any script on calendarspark.com can query the extension and receive a stable per-install fingerprint, enabling cross-session user tracking.
ExtensionDetectForPPContentScript.getMessageListener = function(state) { return function(message) { if (message.origin !== document.location.origin) return; var data = typeof message.data === "string" ? JSON.parse(message.data) : message.data; if (data.from !== ExtensionDetectForPPContentScript.fromExtension && data.status === ExtensionDetectForPPContentScript.requestStatus) { var data_1 = { toolbarId: state.toolbarData.toolbarId, partnerId: state.toolbarData.partnerId, partnerSubId: state.toolbarData.partnerSubId, installDate: state.toolbarData.installDate, toolbarVersion: state.replaceableParams.version, toolbarBuildDate: state.replaceableParams.buildDate, }; window.postMessage(JSON.stringify(ExtensionDetectForPPContentScript.getMessage(state, ExtensionDetectForPPContentScript.requestStatus, data_1)), document.location.origin); } };};Sends a steady stream of telemetry (install, active-ping every 6 hours, errors, config-fetches, notification events) to https://anx.calendarspark.com/anx.gif and /tr.gif encoding the user's toolbarId, partnerId, userSegment, cwsid, coId, etc. as GET query parameters. This is continuous server-side tracking keyed to per-install identifiers; combined with the cookie-setting content script it enables cross-site correlation of the user.
UnifiedLogging.createStandardData = function(eventName, state) { return { anxa: "CAPNative", anxv: state.replaceableParams.toolbarVersion, anxe: eventName, anxt: state.toolbarData.toolbarId, anxtv: state.replaceableParams.toolbarVersion, anxp: state.toolbarData.partnerId, anxsi: state.toolbarData.partnerSubId, anxd: state.replaceableParams.buildDate, f: "00400000", anxr: +new Date(), coid: state.toolbarData.coId, userSegment: state.toolbarData.userSegment };};UnifiedLogging.fireEvent = function(eventName, url, state, eventSpecificData) { return new Promise(function(resolve, reject) { if (!eventName || !url || !state) { reject(new Error("UnifiedLogging: one of the required params is not set")); return; } var data = eventSpecificData ? Object.assign(UnifiedLogging.createStandardData(eventName, state), eventSpecificData) : UnifiedLogging.createStandardData(eventName, state); var searchParamsFromData = UnifiedLogging.getParamsFromData(data); url += ~url.indexOf("?") ? searchParamsFromData : searchParamsFromData.replace("&", "?"); fetch(url)Periodically fetches remote JSON configuration from download.calendarspark.com (notifications-config.json, dailyContent-config.json, pTag params, dormant-service) and persists the response to extension storage where it drives behavior: which notifications to fire, which URLs to open, new search parameters, daily-content opens, and per-cobrand/track config. Remote-controlled behavior without extension updates is an RCE-adjacent pattern — the publisher can alter user-visible behavior (URLs, messages, cadence) server-side at any time.
RemoteConfigLoader.fetchRemoteConfig = function(remoteConfigUrl) { return new Promise(function(resolve, reject) { Logger.log("RemoteConfigLoader: fetchConfig " + remoteConfigUrl); fetch(remoteConfigUrl) .then(function(response) { if (!response.ok) return reject(new Error("error fetching " + remoteConfigUrl + " status: " + response.status)); UnifiedLogging.fireInfoEvent({ message: "config-on-after", topic: "remote-config-loader", data1: remoteConfigUrl }) .catch(Logger.warn); return resolve(response.json()); })Fetches `searchParams` from the remote pTag service (https://params.calendarspark.com/ptag) and injects them into every outgoing search URL built by the new-tab page. The server can remotely change monetization parameters (PC, FROM, PTAG affiliate IDs) attached to every user search, effectively hijacking search revenue attribution on the fly without an extension update.
PTagService.handlePTagServiceResponse = function(pTagServiceResponse) { if (!pTagServiceResponse || !pTagServiceResponse.searchParams || typeof pTagServiceResponse.ttl !== "number") { return Promise.reject(new Error("Invalid response from server: \"" + pTagServiceResponse + "\"")); } var newPtagState = { nextCall: Date.now() + pTagServiceResponse.ttl * 1000, ttl: pTagServiceResponse.ttl }; return Promise.all([StateStorage.get("pTagService"), StateStorage.get(ExtensionSetUp.extensionStateKey)]) .then(function(stateArr) { var savedPtagState = stateArr[0]; var extensionState = stateArr[1]; var newSearchParams = JSON.stringify(pTagServiceResponse.searchParams); var isTTLChanged = newPtagState.ttl !== savedPtagState.ttl; var searchParamsChanged = newSearchParams !== extensionState.toolbarData.searchParams; extensionState.toolbarData.searchParams = newSearchParams;When the user visits any download.*.com landing page (matched via webNavigation.onCompleted), the extension silently fires a 'ToolbarDetect' beacon containing language, partner IDs, toolbar GUID and version to a remote logging endpoint, then rewrites the tab URL to the extension's own new-tab page. This installs a cross-domain user-fingerprint + redirect on site navigation without user interaction.
ExtensionDetectForFP.fireToolbarDetect = function(hashParams, state, dlpEndpoint) { var toolbarDetectData = { anxa: "CAPDownloadProcess", anxl: BrowserUtils.getLanguage(), present: "true", detClassID: "", detPartnerID: hashParams.p2, detSubID: state.replaceableParams.partnerSubID, detToolbarID: state.toolbarData.toolbarId, detToolbarVersion: state.replaceableParams.version, coid: hashParams.coid, anxe: "ToolbarDetect", anxpk: state.replaceableParams.trackID, anxpb: state.replaceableParams.cobrandID, detSubID: state.replaceableParams.partnerSubID, anxpk: state.replaceableParams.trackID }; UnifiedLogging.fireULOnBehalfOfDLP(dlpEndpoint, toolbarDetectData);};A daily alarm automatically opens a new maximized browser window pointing at the extension's new-tab (hp.myway.com/calendarspark) at 2AM local time, regardless of user activity. It also forcibly closes any other tabs pointing at the same new-tab URL. This is unsolicited popup/content-injection behavior scheduled via chrome.alarms, delivering ad/search content on a schedule the user did not consent to.
DailyContentService.openDailyContentWindow = function() { return DailyContentService.queryingForDailyContentOpenTabs() .then(function(tabs) { if (!tabs.length) { return DailyContentService.handlingCaseNoDailyContentOpened(); } if (tabs.length === 1) { return DailyContentService.handlingCaseWithSingleDailyContentTabsAlreadyOpened(tabs[0]); } return DailyContentService.handlingCaseWithMultipleDailyContentTabsAlreadyOpened(tabs); });};Enumerates every open tab and every iframe inside those tabs via chrome.webNavigation.getAllFrames, then scans their URL hashes looking for DLP/toolbar hash markers and harvests partner/campaign IDs from matching iframes. This is broad cross-tab, cross-frame scraping of URL data without any user interaction, going far beyond the declared host permissions.
ExtensionSetUpDLP.getDataFromPageIframe = function(config, defaultToolbarData) { var parentProductHashMatchRegEx = new RegExp(Util.getParentProductParamMatch(config.buildVars.downloadDomain), "i"); var extensionDetectContentScriptMatchRegEx = new RegExp(Util.getExtensionDetectContentScriptMatch(config .buildVars.downloadDomain), "i"); ... for (var _i = 0, tabs_1 = tabs; _i < tabs_1.length; _i++) { var tab = tabs_1[_i]; _loop_1(tab); } return Promise.all(gettingTabIframes) .then(function(results) { for (var _i = 0, results_1 = results; _i < results_1.length; _i++) { var tabIframeDetails = results_1[_i]; if (!tabIframeDetails) continue; for (var _a = 0, tabIframeDetails_1 = tabIframeDetails; _a < tabIframeDetails_1 .length; _a++) { var iframeDetail = tabIframeDetails_1[_a]; var url = new URL(iframeDetail.url);Uses the `cookies` permission to read all cookies from the .calendarspark.com domain and extract a large set of identifiers (toolbarId, partnerId, partnerSubId, coId, countryCode, campaign, cobrand, dlput, installDate, userSegment, etc.). These cookies are then persisted to extension storage and used to construct user fingerprints that are sent to unified-logging endpoints.
ExtensionSetUpDLP.getDataFromCookies = function(domain) { var parseCookies = function(cookies) { var cookiesObj = cookies.reduce(function(obj, cookie) { obj[cookie.name] = cookie.value; return obj; }, {}); var toolbarData = ExtensionSetUpDLP.cleanToolbarData(cookiesObj); Logger.log("ExtensionSetUpDLP: The fetched DLP data looks like: " + JSON.stringify(toolbarData)); return toolbarData; }; return new Promise(function(resolve, reject) { chrome.cookies.getAll({ domain: domain }, function(cookies) { if (cookies.some(function(cookie) { return cookie.name === "toolbarId"; }))Exposes `chrome.management.uninstallSelf` and the ability to override the uninstall survey URL to any script on the *.calendarspark.com origin (via the webTooltabAPIProxy -> webtooltab message bridge). A web page controlled by the publisher can silently uninstall the extension (showConfirmDialog defaults to false) or redirect the user to an arbitrary survey URL, mixing website-level privileges with extension-level privileges.
var features = { management: { uninstall: function(customUninstallOptions) { var uninstall = function() { return new Promise(function(resolve, reject) { var doUninstall = function() { try { var uninstallOptions = { showConfirmDialog: !!customUninstallOptions && customUninstallOptions .showConfirmDialog || false }; ... var result = chrome.management.uninstallSelf(uninstallOptions);A bridge script relays postMessage payloads from the extension's new-tab page (and previously visited offer URLs) directly into the background service worker's message handler, which in turn invokes the webtooltab API (uninstall, disableDaily, getDailyStatus). Any JavaScript the remote new-tab/offer host serves can issue privileged extension commands; the origin check only verifies it's an internal extension URL, but the page content itself is loaded from remote hp.myway.com / calendarspark.com resources.
function onWTTMessage(e) { if (isWebTooltabMessage(e)) { isValidSource() .then(function() { Logger.log("WebToolTabAPIProxy: onWTTMessage: received message " + JSON.stringify(e.data)); var msgToExtensionWTTAPI = { name: "webtooltab", data: JSON.parse(e.data) }; chrome.runtime.sendMessage(msgToExtensionWTTAPI, onWTTMessageResponse); })Fires a conversion tracking pixel (templated from DLP hash params) by injecting an iframe into the new tab page on first open, then removes the iframe. The pixel URL contains partnerId, coId, campaign, cobrand, s2-s5 sub-affiliate identifiers and toolbarId — standard affiliate/conversion tracking beacon for the install.
FirstOpenNT.firePixel = function(state) { return new Promise(function(resolve) { if (!state.toolbarData.pixelUrl || !state.configVars) return resolve(); var pixelURL = FirstOpenNT.replaceChildDomainToParent(state.toolbarData.pixelUrl, state.configVars .downloadDomain); var iframe = document.createElement("iframe"); iframe.addEventListener("load", function(e) { iframe.parentNode.removeChild(iframe); Logger.log("UnifiedLogging: pixel fired " + pixelURL); }, true); iframe.setAttribute("src", pixelURL); document.body.appendChild(iframe); state.toolbarData.pixelUrl = null; resolve(); });};By severity
Versions scanned
Showing 1 of 5 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 13.990.19.63848 | 16 |
Files with findings
13 distinct paths — top paths by unique finding count:
- js/extensionSetUpDLP.js3
- js/extensionDetectForPPContentScript.js2
- js/dailyContentService.js1
- js/extensionDetectForFP.js1
- js/firstOpenNT.js1
- js/localStorageContentScript.js1
- js/newTabToExtensionPageRedirect.js1
- js/otherDomainToExtensionPageRedirect.js1
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.