diff --git a/src/drivers/webextension/html/background.html b/src/drivers/webextension/html/background.html index ef82f1b2c..0bb1bdf41 100644 --- a/src/drivers/webextension/html/background.html +++ b/src/drivers/webextension/html/background.html @@ -6,7 +6,6 @@ -
diff --git a/src/drivers/webextension/images/icons/ActBlue.svg b/src/drivers/webextension/images/icons/ActBlue.svg new file mode 100644 index 000000000..f38bd2503 --- /dev/null +++ b/src/drivers/webextension/images/icons/ActBlue.svg @@ -0,0 +1,15 @@ + diff --git a/src/drivers/webextension/images/icons/AlvandCMS.png b/src/drivers/webextension/images/icons/AlvandCMS.png new file mode 100644 index 000000000..e9e82afb0 Binary files /dev/null and b/src/drivers/webextension/images/icons/AlvandCMS.png differ diff --git a/src/drivers/webextension/images/icons/ApexChat.svg b/src/drivers/webextension/images/icons/ApexChat.svg new file mode 100644 index 000000000..d34b3feb3 --- /dev/null +++ b/src/drivers/webextension/images/icons/ApexChat.svg @@ -0,0 +1,3 @@ + diff --git a/src/drivers/webextension/images/icons/Beehiiv.svg b/src/drivers/webextension/images/icons/Beehiiv.svg new file mode 100644 index 000000000..98b4f2a1e --- /dev/null +++ b/src/drivers/webextension/images/icons/Beehiiv.svg @@ -0,0 +1,29 @@ + diff --git a/src/drivers/webextension/images/icons/Better Stack.svg b/src/drivers/webextension/images/icons/Better Stack.svg new file mode 100644 index 000000000..c8f77db7c --- /dev/null +++ b/src/drivers/webextension/images/icons/Better Stack.svg @@ -0,0 +1,5 @@ + diff --git a/src/drivers/webextension/images/icons/Better Uptime.svg b/src/drivers/webextension/images/icons/Better Uptime.svg deleted file mode 100644 index a136b6056..000000000 --- a/src/drivers/webextension/images/icons/Better Uptime.svg +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/src/drivers/webextension/images/icons/Creatium.svg b/src/drivers/webextension/images/icons/Creatium.svg new file mode 100644 index 000000000..fd85e6d12 --- /dev/null +++ b/src/drivers/webextension/images/icons/Creatium.svg @@ -0,0 +1,4 @@ + diff --git a/src/drivers/webextension/images/icons/DataDome.svg b/src/drivers/webextension/images/icons/DataDome.svg new file mode 100644 index 000000000..1f47aa337 --- /dev/null +++ b/src/drivers/webextension/images/icons/DataDome.svg @@ -0,0 +1,14 @@ + diff --git a/src/drivers/webextension/images/icons/Datatrics.svg b/src/drivers/webextension/images/icons/Datatrics.svg new file mode 100644 index 000000000..05be407e4 --- /dev/null +++ b/src/drivers/webextension/images/icons/Datatrics.svg @@ -0,0 +1,4 @@ + diff --git a/src/drivers/webextension/images/icons/Forethought Solve.svg b/src/drivers/webextension/images/icons/Forethought Solve.svg new file mode 100644 index 000000000..08142234f --- /dev/null +++ b/src/drivers/webextension/images/icons/Forethought Solve.svg @@ -0,0 +1,44 @@ + diff --git a/src/drivers/webextension/images/icons/GreatPages.svg b/src/drivers/webextension/images/icons/GreatPages.svg new file mode 100644 index 000000000..80a623b5c --- /dev/null +++ b/src/drivers/webextension/images/icons/GreatPages.svg @@ -0,0 +1,5 @@ + diff --git a/src/drivers/webextension/images/icons/ID5.svg b/src/drivers/webextension/images/icons/ID5.svg new file mode 100644 index 000000000..a5b59bed4 --- /dev/null +++ b/src/drivers/webextension/images/icons/ID5.svg @@ -0,0 +1,27 @@ + diff --git a/src/drivers/webextension/images/icons/LlamaLink.png b/src/drivers/webextension/images/icons/LlamaLink.png new file mode 100644 index 000000000..23ecaafb0 Binary files /dev/null and b/src/drivers/webextension/images/icons/LlamaLink.png differ diff --git a/src/drivers/webextension/images/icons/Magixite.png b/src/drivers/webextension/images/icons/Magixite.png new file mode 100644 index 000000000..dacd49d28 Binary files /dev/null and b/src/drivers/webextension/images/icons/Magixite.png differ diff --git a/src/drivers/webextension/images/icons/MantisBT.png b/src/drivers/webextension/images/icons/MantisBT.png index 548219842..3eae13dfd 100644 Binary files a/src/drivers/webextension/images/icons/MantisBT.png and b/src/drivers/webextension/images/icons/MantisBT.png differ diff --git a/src/drivers/webextension/images/icons/Mixpanel.svg b/src/drivers/webextension/images/icons/Mixpanel.svg index 1f82faa24..162bcb061 100644 --- a/src/drivers/webextension/images/icons/Mixpanel.svg +++ b/src/drivers/webextension/images/icons/Mixpanel.svg @@ -1,18 +1,3 @@ diff --git a/src/drivers/webextension/images/icons/Moat.svg b/src/drivers/webextension/images/icons/Moat.svg deleted file mode 100644 index 8e4b2ce77..000000000 --- a/src/drivers/webextension/images/icons/Moat.svg +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/src/drivers/webextension/images/icons/Monetate.svg b/src/drivers/webextension/images/icons/Monetate.svg new file mode 100644 index 000000000..dabe4592c --- /dev/null +++ b/src/drivers/webextension/images/icons/Monetate.svg @@ -0,0 +1,14 @@ + diff --git a/src/drivers/webextension/images/icons/OrderCast.svg b/src/drivers/webextension/images/icons/OrderCast.svg new file mode 100644 index 000000000..01074327c --- /dev/null +++ b/src/drivers/webextension/images/icons/OrderCast.svg @@ -0,0 +1,11 @@ + diff --git a/src/drivers/webextension/images/icons/Progress.svg b/src/drivers/webextension/images/icons/Progress.svg new file mode 100644 index 000000000..1ada67cb1 --- /dev/null +++ b/src/drivers/webextension/images/icons/Progress.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/drivers/webextension/images/icons/Salla.svg b/src/drivers/webextension/images/icons/Salla.svg index 89a54c19b..e362bf3ed 100644 --- a/src/drivers/webextension/images/icons/Salla.svg +++ b/src/drivers/webextension/images/icons/Salla.svg @@ -1,19 +1,4 @@ diff --git a/src/drivers/webextension/images/icons/SellersCommerce.png b/src/drivers/webextension/images/icons/SellersCommerce.png new file mode 100644 index 000000000..ac0ec3463 Binary files /dev/null and b/src/drivers/webextension/images/icons/SellersCommerce.png differ diff --git a/src/drivers/webextension/images/icons/Shoppub.svg b/src/drivers/webextension/images/icons/Shoppub.svg new file mode 100644 index 000000000..448244baf --- /dev/null +++ b/src/drivers/webextension/images/icons/Shoppub.svg @@ -0,0 +1,5 @@ + diff --git a/src/drivers/webextension/images/icons/Skyflow.svg b/src/drivers/webextension/images/icons/Skyflow.svg new file mode 100644 index 000000000..1298bf4d6 --- /dev/null +++ b/src/drivers/webextension/images/icons/Skyflow.svg @@ -0,0 +1,4 @@ + diff --git a/src/drivers/webextension/images/icons/Spatie.png b/src/drivers/webextension/images/icons/Spatie.png deleted file mode 100644 index 7da240289..000000000 Binary files a/src/drivers/webextension/images/icons/Spatie.png and /dev/null differ diff --git a/src/drivers/webextension/images/icons/Spatie.svg b/src/drivers/webextension/images/icons/Spatie.svg new file mode 100644 index 000000000..63f947819 --- /dev/null +++ b/src/drivers/webextension/images/icons/Spatie.svg @@ -0,0 +1 @@ + diff --git a/src/drivers/webextension/images/icons/Tiiny Host.png b/src/drivers/webextension/images/icons/Tiiny Host.png new file mode 100644 index 000000000..3591c72df Binary files /dev/null and b/src/drivers/webextension/images/icons/Tiiny Host.png differ diff --git a/src/drivers/webextension/images/icons/WayForPay.svg b/src/drivers/webextension/images/icons/WayForPay.svg new file mode 100644 index 000000000..fd0a65ccf --- /dev/null +++ b/src/drivers/webextension/images/icons/WayForPay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/drivers/webextension/images/icons/authorize.net.svg b/src/drivers/webextension/images/icons/authorize.net.svg new file mode 100644 index 000000000..f2824846e --- /dev/null +++ b/src/drivers/webextension/images/icons/authorize.net.svg @@ -0,0 +1,12 @@ + diff --git a/src/drivers/webextension/images/icons/datadome.png b/src/drivers/webextension/images/icons/datadome.png deleted file mode 100644 index 76ec66a29..000000000 Binary files a/src/drivers/webextension/images/icons/datadome.png and /dev/null differ diff --git a/src/drivers/webextension/js/background.js b/src/drivers/webextension/js/background.js index 1743427fc..c762b7cb0 100644 --- a/src/drivers/webextension/js/background.js +++ b/src/drivers/webextension/js/background.js @@ -3,4 +3,3 @@ importScripts(chrome.runtime.getURL('js/wappalyzer.js')) importScripts(chrome.runtime.getURL('js/utils.js')) importScripts(chrome.runtime.getURL('js/driver.js')) -importScripts(chrome.runtime.getURL('js/lib/network.js')) diff --git a/src/drivers/webextension/js/driver.js b/src/drivers/webextension/js/driver.js index c5fa8b67f..a22fe5d96 100644 --- a/src/drivers/webextension/js/driver.js +++ b/src/drivers/webextension/js/driver.js @@ -17,7 +17,7 @@ const expiry = 1000 * 60 * 60 * 48 const maxHostnames = 100 const hostnameIgnoreList = - /\b((local|dev(elop(ment)?)?|sandbox|stag(e|ing)?|preprod|production|preview|test(ing)?|[^a-z]demo(shop)?|cache)[.-]|dev\d|localhost|((wappalyzer|google|bing|baidu|microsoft|duckduckgo|facebook|adobe|twitter|reddit|yahoo|wikipedia|amazon|amazonaws|youtube|stackoverflow|github|stackexchange|w3schools|twitch)\.)|(live|office|herokuapp|shopifypreview)\.com|\.local|\.test|\.netlify\.app|web\.archive\.org|zoom\.us|^([0-9.]+|[\d.]+)$|^([a-f0-9:]+:+)+[a-f0-9]+$)/ + /\b((local|dev(elop(ment)?)?|sandbox|stag(e|ing)?|preprod|production|preview|test(ing)?|[^a-z]demo(shop)?|cache)[.-]|dev\d|localhost|((wappalyzer|google|bing|baidu|microsoft|duckduckgo|facebook|adobe|twitter|reddit|yahoo|wikipedia|amazon|amazonaws|youtube|stackoverflow|github|stackexchange|w3schools|twitch)\.)|(live|office|herokuapp|shopifypreview)\.com|\.local|\.test|\.netlify\.app|ngrok|web\.archive\.org|zoom\.us|^([0-9.]+|[\d.]+)$|^([a-f0-9:]+:+)+[a-f0-9]+$)/ const xhrDebounce = [] @@ -79,7 +79,6 @@ const Driver = { {} ), robots: await getOption('robots', {}), - ads: [], } const { version } = chrome.runtime.getManifest() @@ -711,14 +710,6 @@ const Driver = { Driver.log({ hostname, technologies: resolved }) }, - /** - * Callback for onAd listener - * @param {Object} ad - */ - onAd(ad) { - Driver.cache.ads.push(ad) - }, - /** * Update the extension icon * @param {String} url @@ -1009,20 +1000,6 @@ const Driver = { Driver.cache.hostnames[hostname].hits = 0 }) } - - if (Driver.cache.ads.length > 25) { - try { - await Driver.post( - 'https://ad.wappalyzer.com/log/wp/', - Driver.cache.ads - ) - } catch (error) { - // eslint-disable-next-line no-console - console.error(error) - } - - Driver.cache.ads = [] - } } }, } diff --git a/src/drivers/webextension/js/lib/iframe.js b/src/drivers/webextension/js/lib/iframe.js deleted file mode 100644 index c883e4ffb..000000000 --- a/src/drivers/webextension/js/lib/iframe.js +++ /dev/null @@ -1,1546 +0,0 @@ -'use strict' -/* eslint-env browser */ -/* globals chrome */ -;(function (win) { - const exports = {} - - ;(function (exports) { - const utils = { - /** - * Normalize URL - * @param {String} url - */ - normalizeUrl(url) { - return this.hashUrl(url) || null - }, - - /** - * Get referrer. - */ - getReferrer() { - return this.normalizeUrl(document.referrer) - }, - - /** - * Get current page URL. - */ - getPageUrl() { - return this.normalizeUrl(window.location.href) - }, - - /** - * Generated hashed URL. - * @param {String} url - */ - hashUrl(url) { - let a, result - - if (!url || url.indexOf('http') !== 0) { - return null - } - - a = document.createElement('a') - a.href = url - - result = a.protocol + '//' + a.hostname + '/' - - if (a.pathname && a.pathname !== '/') { - result += this.hashCode(a.pathname) - } - - if (a.search) { - result += '?' + this.hashCode(a.search) - } - - if (a.hash) { - result += '#' + this.hashCode(a.hash) - } - - return result - }, - - /** - * Generate random hash. - * @param {String} str - */ - hashCode(str) { - let hash = 0 - let kar - let i - - if (str.length === 0) { - return hash - } - - for (i = 0; i < str.length; i++) { - kar = str.charCodeAt(i) - hash = (hash << 5) - hash + kar - hash = hash & hash - } - - return hash + Math.pow(2, 32) - }, - - /** - * Apply array function to non-array. - * @param {Object} a - */ - realArray(a) { - return Array.prototype.slice.apply(a) - }, - - /** - * Listener callback for onDocLoaded. - * @param {Object} doc - * @param {Function} callback - */ - onDocLoaded(doc, callback) { - if (doc.readyState === 'loading') { - doc.addEventListener('DOMContentLoaded', callback) - } else { - callback() - } - }, - - SCRIPT_IN_WINDOW_TOP: window === window.top, - - /** - * Check for href Window object. - * @param {Object} win - */ - isFriendlyWindow(win) { - let href - try { - href = win.location.href - } catch (e) { - return false - } - return true - }, - - /** - * Get default view from element. - * @param {Object} el - */ - elementWindow(el) { - return el.ownerDocument.defaultView - }, - - /** - * Get viewport size. - * @param {Object} win - */ - viewport(win) { - return { width: win.innerWidth, height: win.innerHeight } - }, - - /** - * Parse query string parameters. - * @param {String} qs - */ - parseQS(qs) { - if (qs.indexOf('http') === 0) { - qs = qs.split('?')[1] - } - let i, kvs, key, val - const dict = {} - qs = qs.split('&') - for (i = 0; i < qs.length; i++) { - kvs = qs[i].split('=') - key = kvs[0] - val = kvs.slice(1).join('=') - try { - dict[key] = window.decodeURIComponent(val) - } catch (e) { - continue - } - } - return dict - }, - - /** - * Send PostMessage response. - * @param {Object} message - * @param {String} event - * @param {String} responseMessage - */ - sendToBackground(message, event, responseMessage) { - try { - chrome.runtime.sendMessage(message, (message) => { - if (message && typeof message.tracking_enabled !== 'undefined') { - if (message.tracking_enabled) { - utilCallback() - } else { - utilElseCallback() - } - } - }) - } catch (error) { - // Continue - } - }, - - /** - * Check if anonymous tracking is enabled. - * @param {Function} callback - * @param {Function} elseCallback - * @todo validate if utilCallback or utilElseCallback are being used. - */ - askIfTrackingEnabled(callback, elseCallback) { - utilCallback = callback - utilElseCallback = elseCallback - - this.sendToBackground( - 'is_tracking_enabled', - '', - 'tracking_enabled_response' - ) - }, - } - - utils.SCRIPT_IN_FRIENDLY_IFRAME = - !utils.SCRIPT_IN_WINDOW_TOP && utils.isFriendlyWindow(window.parent) - utils.SCRIPT_IN_HOSTILE_IFRAME = - !utils.SCRIPT_IN_WINDOW_TOP && !utils.SCRIPT_IN_FRIENDLY_IFRAME - - /** - * Generate new Logging object. - */ - function LogGenerator() { - this.msgNum = 0 - this.pageMeta = { - url: utils.getPageUrl(), - isHP: window.location.pathname === '/', - referrer: utils.getReferrer(), - rand: Math.floor(Math.random() * 10e12), - startTime: new Date().getTime(), - } - } - - LogGenerator.prototype = { - /** - * Log data. - * @param {String} event - * @param {Array} opt_assets - * @param {Array} opt_pageTags - */ - log(event, opt_assets, opt_pageTags) { - let opt_video_assets - if (event === 'video' || event === 'invalid-video') { - opt_video_assets = opt_assets || [] - opt_assets = [] - } else { - opt_video_assets = [] - opt_assets = opt_assets || [] - } - const result = { - doc: this.pageMeta, - event, - video_assets: opt_video_assets, - assets: opt_assets, - version: '3', - mrev: '15a9f21-d', - msgNum: this.msgNum, - timestamp: new Date().getTime(), - pageVis: document.visibilityState, - pageFoc: document.hasFocus(), - pageTags: opt_pageTags || [], - } - this.msgNum++ - return result - }, - } - - utils.LogGenerator = LogGenerator - - let utilCallback, utilElseCallback - - exports.utils = utils - })(exports) - ;(function (exports) { - const SizeMatcher = { - VALID_AD_SIZES: [ - [300, 50], - [320, 50], - [160, 600], - [300, 250], - [300, 600], - [300, 1050], - [336, 280], - [336, 850], - [468, 60], - [728, 90], - [728, 250], - [728, 270], - [970, 66], - [970, 90], - [970, 125], - [970, 250], - [970, 400], - [970, 415], - [1280, 100], - ], - - PX_SIZE_TOL: 10, - - /** - * Get ad size. - * @param {Int} width - * @param {Int} height - */ - getMatchedAdSize(width, height) { - if (!this.set) { - this.set = this._makeSizeSet() - } - - return this.set[Math.round(width) + 'x' + Math.round(height)] - }, - - /** - * Check element size. - * @param {HTMLElement} el - */ - elementIsAdShaped(el) { - return !!this.getMatchedAdSizeForElement(el) - }, - - /** - * Get ad size. - * @param {HTMLElement} el - * @todo better description - */ - getMatchedAdSizeForElement(el) { - const rect = el.getBoundingClientRect() - return this.getMatchedAdSize(rect.width, rect.height) - }, - - /** - * Generate ad sizes. - */ - _makeSizeSet() { - const set = {} - let i - let xfuz - let yfuz - let size - let width - let height - - for (i = 0; i < this.VALID_AD_SIZES.length; i++) { - for (xfuz = -this.PX_SIZE_TOL; xfuz <= this.PX_SIZE_TOL; xfuz++) { - for (yfuz = -this.PX_SIZE_TOL; yfuz <= this.PX_SIZE_TOL; yfuz++) { - size = this.VALID_AD_SIZES[i] - width = size[0] + xfuz - height = size[1] + yfuz - set[width + 'x' + height] = size - } - } - } - return set - }, - } - - const Throttler = { - MAX_SEARCHES_PER_WINDOW: 10, - MAX_SEARCHES_PER_ELEMENT: 2, - - /** - * Count number of elements. - * @param {HTMLElement} el - */ - countSearch: (el) => { - if (typeof el.searches !== 'number') { - el.searches = 0 - } - - el.searches += 1 - }, - - /** - * - * @param {*} el - * @param {*} max - * - * @todo add description - */ - throttle(el, max) { - if (typeof el.searches === 'number' && el.searches >= max) { - return true - } - return false - }, - - /** - * - * @param {*} el - * - * @todo add description - */ - throttleElement(el) { - return this.throttle(el, this.MAX_SEARCHES_PER_ELEMENT) - }, - - /** - * - * @param {*} win - * - * @todo add description - */ - throttleWin(win) { - return this.throttle(win, this.MAX_SEARCHES_PER_WINDOW) - }, - - /** - * - * @param {*} el - * - * @todo add description - */ - getCount(el) { - return el.searches || 0 - }, - } - - /** - * Initialize window and document elements. - * @param {*} win - */ - function TopSearcher(win) { - this.win = win - this.doc = win.document - } - - /** - * Add search function. - */ - TopSearcher.prototype.search = function () { - const candidates = exports.utils.realArray( - this.doc.querySelectorAll('img, object, embed') - ) - let html5Ad - let ads = [] - - ads = ads.concat( - candidates.filter(function (el) { - if (!el.mpAdFound && !Throttler.throttleElement(el)) { - Throttler.countSearch(el) - if ( - (el.tagName !== 'IMG' || isStandardImage(el)) && - SizeMatcher.elementIsAdShaped(el) - ) { - el.mpAdFound = true - return true - } - } - return false - }) - ) - - html5Ad = this._mainGetHTMLAd() - if (html5Ad) { - html5Ad.html5 = true - html5Ad.mpAdFound = true - ads.push(html5Ad) - } - - return ads - } - - /** - * @todo add description - */ - TopSearcher.prototype._mainGetHTMLAd = function () { - const styles = this.doc.querySelectorAll( - 'div > style, div > link[rel="stylesheet"]' - ) - let i - let div - for (i = 0; i < styles.length; i++) { - div = styles[i].parentNode - if ( - !div.mpAdFound && - SizeMatcher.elementIsAdShaped(div) && - this._jumpedOut(div) - ) { - return div - } - } - } - - /** - * @todo add description - */ - TopSearcher.prototype._jumpedOut = function (el) { - let siblings, ifrs - siblings = exports.utils.realArray(el.parentNode.children) - ifrs = siblings.filter(function (el) { - return ( - el.tagName === 'IFRAME' && - el.offsetWidth === 0 && - el.offsetHeight === 0 - ) - }) - return ifrs.length > 0 - } - - /** - * - * @param {*} win - * - * @todo add description - */ - function IframeSearcher(win) { - this.MIN_AD_AREA = 14000 - this.MIN_WINDOW_PX = 10 - - this.win = win - this.doc = win.document - this.body = win.document.body - this.winClickTag = win.clickTag - this.adSizeMeta = this._getAdSizeMeta() - this.numElementsInBody = - (this.body && this.body.querySelectorAll('*').length) || 0 - - this.shouldSearchWindow = false - if ( - !this.win.mpAdFound && - this.body && - !Throttler.throttleWin(this.win) - ) { - this.winWidth = this.win.innerWidth - this.winHeight = this.win.innerHeight - if ( - this._meetsMinAdSize(this.winWidth, this.winHeight) && - !this._containsLargeIframes() - ) { - this.shouldSearchWindow = true - } - } - } - - /** - * @todo add description - */ - IframeSearcher.prototype.search = function () { - let ad - - if (this.shouldSearchWindow) { - ad = this._search() - if (ad) { - ad.mpAdFound = true - win.mpAdFound = true - return ad - } - Throttler.countSearch(this.win) - } - - return null - } - - /** - * @todo add description - */ - IframeSearcher.prototype._search = function () { - const _this = this - let stdCandidates - let html5Candidates - let stdEl - let html5El - - stdCandidates = this.body.querySelectorAll('img, object, embed') - - stdEl = getFirst(stdCandidates, function (el) { - if ( - !el.mpAdFound && - !Throttler.throttleElement(el) && - (el.tagName !== 'IMG' || isStandardImage(el)) && - _this._elementIsAtLeastAsBigAsWindow(el) - ) { - return true - } - Throttler.countSearch(el) - return false - }) - - if (stdEl) { - return stdEl - } - - if (this._isHTML5Iframe()) { - html5Candidates = this.doc.querySelectorAll( - 'body, canvas, button, video, svg, div' - ) - html5El = getFirst(html5Candidates, function (el) { - if (_this._elementIsAtLeastAsBigAsWindow(el)) { - return true - } - Throttler.countSearch(el) - return false - }) - } - - if (html5El) { - html5El.html5 = true - html5El.winClickTag = this.winClickTag - html5El.adSizeMeta = this.adSizeMeta - return html5El - } - - return null - } - - /** - * @todo add description - */ - IframeSearcher.prototype._isHTML5Iframe = function () { - if (this.winClickTag || this.adSizeMeta) { - return true - } - - if ( - this.doc.querySelectorAll('canvas', 'button', 'video', 'svg').length > 0 - ) { - return true - } - - if ( - this.numElementsInBody >= 5 && - Throttler.getCount(this.win) > 0 && - this.doc.querySelectorAll('div').length > 0 - ) { - return true - } - - return false - } - - /** - * @todo add description - */ - IframeSearcher.prototype._elementIsAtLeastAsBigAsWindow = function (el) { - const rect = el.getBoundingClientRect() - const tol = 0.95 - - return ( - rect.width >= tol * this.winWidth && rect.height >= tol * this.winHeight - ) - } - - /** - * @todo add description - */ - IframeSearcher.prototype._meetsMinAdSize = function (width, height) { - return width * height >= this.MIN_AD_AREA - } - - /** - * @todo add description - */ - IframeSearcher.prototype._containsLargeIframes = function () { - const iframes = this.doc.querySelectorAll('iframe') - let rect - let i - for (i = 0; i < iframes.length; i++) { - rect = iframes[i].getBoundingClientRect() - if ( - rect.width > this.MIN_WINDOW_PX || - rect.height > this.MIN_WINDOW_PX - ) { - return true - } - } - return false - } - - /** - * @todo add description - */ - IframeSearcher.prototype._getAdSizeMeta = function () { - const adSizeMeta = this.doc.querySelectorAll('meta[name="ad.size"]') - if (adSizeMeta.length > 0) { - return adSizeMeta[0].content - } else { - return null - } - } - - /** - * - * @param {*} arr - * @param {*} testFn - * - * @todo add description - */ - function getFirst(arr, testFn) { - let i, el - for (i = 0; i < arr.length; i++) { - el = arr[i] - if (testFn(el)) { - return el - } - } - return null - } - - /** - * Check for image attributes. - * @param {HTMLElement} img - */ - function isStandardImage(img) { - return ( - img.src && - (img.parentNode.tagName === 'A' || img.getAttribute('onclick')) - ) - } - - /** - * Extract iFrames from page. - * @param {Object} win - */ - function getFriendlyIframes(win) { - let iframes = win.document.querySelectorAll('iframe') - iframes = exports.utils.realArray(iframes) - const friendlyIframes = iframes.filter(function (ifr) { - return exports.utils.isFriendlyWindow(ifr.contentWindow) - }) - return friendlyIframes - } - - /** - * - * @param {*} win - */ - function findAds(win) { - let i - let iframes - let searcher - let ad - let ads = [] - - if (win === win.top) { - searcher = new TopSearcher(win) - ads = ads.concat(searcher.search()) - } else { - searcher = new IframeSearcher(win) - ad = searcher.search() - if (ad) { - ads.push(ad) - } - } - - iframes = getFriendlyIframes(win) - for (i = 0; i < iframes.length; i++) { - ads = ads.concat(findAds(iframes[i].contentWindow)) - } - - return ads - } - - exports.adfinder = { - getMatchedAdSize: SizeMatcher.getMatchedAdSize.bind(SizeMatcher), - findAds, - } - })(exports) - ;(function (exports) { - const parser = { - TAGS_WITH_SRC_ATTR: { - IMG: true, - SCRIPT: true, - IFRAME: true, - EMBED: true, - }, - - MAX_ATTR_LEN: 100, - - /** - * - * @param {*} el - * @param {*} params - * - * @todo add description - */ - getUrl(el, params) { - let url - - if (this.TAGS_WITH_SRC_ATTR.hasOwnProperty(el.tagName)) { - url = el.src - } else if (el.tagName === 'OBJECT') { - url = el.data || (params && params.movie) || null - } else if (el.tagName === 'A') { - url = el.href - } - - if (url && url.indexOf('http') === 0) { - return url - } else { - return null - } - }, - - /** - * - * @param {*} el - * - * @todo add description - */ - getParams(el) { - if (el.tagName !== 'OBJECT') { - return null - } - - let i, child - const params = {} - const children = el.children - for (i = 0; i < children.length; i++) { - child = children[i] - if (child.tagName === 'PARAM' && child.name) { - params[child.name.toLowerCase()] = child.value - } - } - return params - }, - - /** - * Get element position. - * @param {HTMLElement} el - */ - getPosition(el) { - const rect = el.getBoundingClientRect() - const win = exports.utils.elementWindow(el) - - return { - width: Math.round(rect.width), - height: Math.round(rect.height), - left: Math.round(rect.left + win.pageXOffset), - top: Math.round(rect.top + win.pageYOffset), - } - }, - - /** - * - * @param {*} el - * @param {*} params - * @param {*} url - * - * @todo add description - */ - getFlashvars(el, params, url) { - let flashvars - const urlQS = url && url.split('?')[1] - - if (el.tagName === 'EMBED') { - flashvars = el.getAttribute('flashvars') || urlQS - } else if (el.tagName === 'OBJECT') { - flashvars = params.flashvars || el.getAttribute('flashvars') || urlQS - } - - return (flashvars && exports.utils.parseQS(flashvars)) || null - }, - - /** - * - * @param {*} el - * @param {*} flashvars - * - * @todo add description - */ - findClickThru(el, flashvars) { - let key - if (el.tagName === 'IMG' && el.parentElement.tagName === 'A') { - return el.parentElement.href - } else if (flashvars) { - for (key in flashvars) { - if (flashvars.hasOwnProperty(key)) { - if (key.toLowerCase().indexOf('clicktag') === 0) { - return flashvars[key] - } - } - } - } - return null - }, - - /** - * Get element attribute. - * @param {HTMLElement} el - * @param {String} name - */ - getAttr(el, name) { - const val = el.getAttribute(name) - - if (val && val.slice && val.toString) { - return val.slice(0, this.MAX_ATTR_LEN).toString() - } else { - return null - } - }, - - /** - * - * @param {*} obj - * @param {*} name - * @param {*} val - * - * @todo add description - */ - putPropIfExists(obj, name, val) { - if (val) { - obj[name] = val - } - }, - - /** - * - * @param {*} obj - * @param {*} el - * @param {*} name - * - * @todo add description - */ - putAttrIfExists(obj, el, name) { - const val = this.getAttr(el, name) - this.putPropIfExists(obj, name, val) - }, - - /** - * Convert Element to JSON - * @param {HTMLElement} el - * @param {Boolean} opt_findClickThru - */ - elementToJSON(el, opt_findClickThru) { - const pos = this.getPosition(el) - const params = this.getParams(el) - const url = this.getUrl(el, params) - const flashvars = this.getFlashvars(el, params, url) - const clickThru = opt_findClickThru && this.findClickThru(el, flashvars) - const json = { - tagName: el.tagName, - width: pos.width, - height: pos.height, - left: pos.left, - top: pos.top, - children: [], - } - - if (params) { - delete params.flashvars - } - - this.putAttrIfExists(json, el, 'id') - this.putAttrIfExists(json, el, 'class') - this.putAttrIfExists(json, el, 'name') - - this.putPropIfExists(json, 'flashvars', flashvars) - this.putPropIfExists(json, 'url', url) - this.putPropIfExists(json, 'params', params) - this.putPropIfExists(json, 'clickThru', clickThru) - - return json - }, - } - - exports.parser = { elementToJSON: parser.elementToJSON.bind(parser) } - })(exports) - - // Anonymous invocation. - ;(function (exports) { - /** - * Setter for ad data. - * @param {*} adData - */ - const ContextManager = function (adData) { - this.adData = adData - } - - ContextManager.prototype = { - CONTAINER_SIZE_TOL: 0.4, - ASPECT_RATIO_FOR_LEADERBOARDS: 2, - - /** - * Check if iframe is valid. - * @param {HTMLElement} el - * @param {HTMLElement} opt_curWin - */ - isValidContainer(el, opt_curWin) { - const cWidth = el.clientWidth - const cHeight = el.clientHeight - - const adWidth = this.adData.width - const adHeight = this.adData.height - - const winWidth = opt_curWin && opt_curWin.innerWidth - const winHeight = opt_curWin && opt_curWin.innerHeight - const similarWin = - opt_curWin && - this.withinTol(adWidth, winWidth) && - this.withinTol(adHeight, winHeight) - - const similarSizeX = this.withinTol(adWidth, cWidth) - const similarSizeY = this.withinTol(adHeight, cHeight) - const adAspect = adWidth / adHeight - - return ( - similarWin || - el.tagName === 'A' || - (adAspect >= this.ASPECT_RATIO_FOR_LEADERBOARDS && similarSizeY) || - (similarSizeX && similarSizeY) - ) - }, - - /** - * Check tolerance. - * @param {Int} adlen - * @param {Int} conlen - */ - withinTol(adlen, conlen) { - const pct = (conlen - adlen) / adlen - - return pct <= this.CONTAINER_SIZE_TOL - }, - - /** - * Serialize elements. - * @param {*} el - * @todo define parameter type. - */ - serializeElements(el) { - if (!el) { - return - } - let i - let ifrWin - const adId = this.adData.adId - let elIsAd = false - - if (adId && el[adId] && el[adId].isAd === true) { - elIsAd = true - } - - const json = exports.parser.elementToJSON(el, elIsAd) - let childJSON - - if (elIsAd) { - json.adId = adId - this.adData.element = {} - - const keys = Object.keys(json) - for (i = 0; i < keys.length; i++) { - const key = keys[i] - if (key !== 'children' && key !== 'contents') { - this.adData.element[key] = json[key] - } - } - } - - const children = exports.utils - .realArray(el.children) - .filter(function (el) { - const param = el.tagName === 'PARAM' - const inlineScript = - el.tagName === 'SCRIPT' && !(el.src && el.src.includes('http')) - const noScript = el.tagName === 'NOSCRIPT' - return !(param || inlineScript || noScript) - }) - - for (i = 0; i < children.length; i++) { - childJSON = this.serializeElements(children[i]) - if (childJSON) { - json.children.push(childJSON) - } - } - - if (el.tagName === 'IFRAME') { - ifrWin = el.contentWindow - - if (adId && el[adId] && el[adId].needsWindow) { - json.contents = this.adData.serializedIframeContents - el[adId].needsWindow = false - delete this.adData.serializedIframeContents - } else if (exports.utils.isFriendlyWindow(ifrWin)) { - childJSON = this.serializeElements(ifrWin.document.documentElement) - if (childJSON) { - json.contents = childJSON - } - } - } - - if ( - json.children.length > 0 || - json.adId || - json.tagName === 'IFRAME' || - json.url - ) { - return json - } else { - return null - } - }, - - /** - * Get element containers. - * @param {*} containerEl - */ - captureHTML(containerEl) { - this.adData.context = this.serializeElements(containerEl) - }, - - /** - * Get number of Nodes. - * @param {HTMLElement} el - */ - nodeCount(el) { - return el.getElementsByTagName('*').length + 1 - }, - - /** - * - * @param {*} curWin - * @param {*} referenceElement - * - * @todo add description - */ - highestContainer(curWin, referenceElement) { - let curContainer = referenceElement - const docEl = curWin.document.documentElement - let parentContainer - - if (curWin !== curWin.top && this.isValidContainer(docEl, curWin)) { - return docEl - } - - while (true) { - parentContainer = curContainer.parentElement - if (parentContainer && this.isValidContainer(parentContainer)) { - curContainer = parentContainer - } else { - return curContainer - } - } - }, - } - - const tagfinder = { - /** - * - * @param {*} adData - * @param {*} opt_el - * @param {*} opt_winPos - * - * @todo add description - */ - setPositions(adData, opt_el, opt_winPos) { - const el = opt_el || adData.context - const winPos = opt_winPos || { left: 0, top: 0 } - let ifrPos - - el.left += winPos.left - el.top += winPos.top - - if (el.children) { - el.children.forEach(function (child) { - this.setPositions(adData, child, winPos) - }, this) - } - - if (el.contents) { - ifrPos = { left: el.left, top: el.top } - this.setPositions(adData, el.contents, ifrPos) - } - - if (el.adId === adData.adId) { - adData.element.left = el.left - adData.element.top = el.top - } - }, - - /** - * - * @param {*} adData - * @param {*} referenceElement - * - * @todo add description - */ - appendTags: (adData, referenceElement) => { - const mgr = new ContextManager(adData) - let curWin = exports.utils.elementWindow(referenceElement) - let highestContainer - - while (true) { - highestContainer = mgr.highestContainer(curWin, referenceElement) - mgr.captureHTML(highestContainer) - if (curWin === curWin.top) { - break - } else { - curWin.mpAdFound = true - - mgr.adData.serializedIframeContents = mgr.adData.context - - if (exports.utils.isFriendlyWindow(curWin.parent)) { - referenceElement = curWin.frameElement - referenceElement[mgr.adData.adId] = { needsWindow: true } - curWin = curWin.parent - } else { - break - } - } - } - return { - referenceElement, - highestContainer, - } - }, - } - - exports.tagfinder = tagfinder - })(exports) - ;(function (exports) { - let _onAdFound - const _logGen = new exports.utils.LogGenerator() - let _pageTags - const INIT_MS_BW_SEARCHES = 2000 - const PAGE_TAG_RE = new RegExp('gpt|oascentral') - const POST_MSG_ID = '1554456894-8541-12665-19466-15909' - const AD_SERVER_RE = new RegExp('^(google_ads_iframe|oas_frame|atwAdFrame)') - - /** - * Get script tags from document. - * @param {Object} doc - */ - function getPageTags(doc) { - let scripts = doc.getElementsByTagName('script') - const pageTags = [] - scripts = exports.utils.realArray(scripts) - scripts.forEach(function (script) { - if (PAGE_TAG_RE.exec(script.src)) { - pageTags.push({ tagName: 'SCRIPT', url: script.src }) - } - }) - return pageTags - } - - /** - * Send message to parent iFrames. - * @param {String} adData - */ - function messageAllParentFrames(adData) { - adData.postMessageId = POST_MSG_ID - - adData = JSON.stringify(adData) - - let win = window - while (win !== win.top) { - win = win.parent - win.postMessage(adData, '*') - } - } - - /** - * - * @param {String} adData - * @param {HTMLElement} referenceElement - * - * @todo update description - */ - function appendTagsAndSendToParent(adData, referenceElement) { - const results = exports.tagfinder.appendTags(adData, referenceElement) - if (exports.utils.SCRIPT_IN_HOSTILE_IFRAME) { - messageAllParentFrames(adData) - } else if (exports.utils.SCRIPT_IN_WINDOW_TOP) { - exports.tagfinder.setPositions(adData) - - adData.matchedSize = exports.adfinder.getMatchedAdSize( - adData.width, - adData.height - ) - if (!adData.matchedSize) { - if (AD_SERVER_RE.exec(results.referenceElement.id)) { - adData.matchedSize = [adData.width, adData.height] - adData.oddSize = true - } else { - return - } - } - delete adData.width - delete adData.height - adData.curPageUrl = exports.utils.getPageUrl() - _pageTags = _pageTags || getPageTags(document) - const log = _logGen.log('ad', [adData], _pageTags) - - if (_onAdFound) { - _onAdFound(log, results.referenceElement) - } - } - } - - /** - * SetTimeout wrapper for extracting ads. - */ - function extractAdsWrapper() { - if ( - exports.utils.SCRIPT_IN_WINDOW_TOP || - document.readyState === 'complete' - ) { - extractAds() - } - setTimeout(function () { - extractAdsWrapper() - }, INIT_MS_BW_SEARCHES) - } - - /** - * Main function for extracting ads after loaded. - */ - function extractAds() { - const ads = exports.adfinder.findAds(window) - ads.forEach(function (ad) { - const startTime = new Date().getTime() - const adId = startTime + '-' + Math.floor(Math.random() * 10e12) - - const adData = { - width: Math.round(ad.offsetWidth), - height: Math.round(ad.offsetHeight), - startTime, - adId, - html5: ad.html5 || false, - } - - if (ad.html5) { - adData.adSizeMeta = ad.adSizeMeta || null - adData.winClickTag = ad.winClickTag || null - } - - ad[adId] = { isAd: true } - - appendTagsAndSendToParent(adData, ad) - }) - } - - /** - * Check if window is child of parent. - * @param {Object} myWin - * @param {Object} otherWin - */ - function isChildWin(myWin, otherWin) { - let parentWin = otherWin.parent - while (parentWin !== otherWin) { - if (parentWin === myWin) { - return true - } - otherWin = parentWin - parentWin = parentWin.parent - } - return false - } - - /** - * - * @param {*} win - * @param {*} winToMatch - * - * @todo update description - */ - function iframeFromWindow(win, winToMatch) { - let i - let ifr - let ifrWin - const iframes = win.document.querySelectorAll('iframe') - - for (i = 0; i < iframes.length; i++) { - ifr = iframes[i] - if (ifr.contentWindow === winToMatch) { - return ifr - } - } - - for (i = 0; i < iframes.length; i++) { - ifrWin = iframes[i].contentWindow - if (exports.utils.isFriendlyWindow(ifrWin)) { - ifr = iframeFromWindow(ifrWin, winToMatch) - if (ifr) { - return ifr - } - } - } - } - - /** - * - * @param {*} event - * - * @todo update description - */ - function onPostMessage(event) { - let adData - const ifrWin = event.source - const myWin = window.document.defaultView - let ifrTag - - if (typeof event.data === 'string' && event.data.includes(POST_MSG_ID)) { - try { - adData = JSON.parse(event.data) - } catch (e) { - return - } - } else return - - if (adData.postMessageId === POST_MSG_ID) { - delete adData.postMessageId - - event.stopImmediatePropagation() - - if (isChildWin(myWin, ifrWin)) { - if (exports.utils.isFriendlyWindow(ifrWin)) { - ifrTag = ifrWin.frameElement - } else { - ifrTag = iframeFromWindow(myWin, ifrWin) - } - - if (ifrTag) { - ifrTag[adData.adId] = { needsWindow: true } - appendTagsAndSendToParent(adData, ifrTag) - } - } - } - } - - /** - * - * @param {*} msg - * @param {*} sender - * @param {*} callback - * - * @todo update description - */ - function onVideoMessage(msg, sender, callback) { - let log - if (msg.event === 'new-video-ad') { - msg.assets.forEach(function (asset) {}) - log = _logGen.log('video', msg.assets) - } else { - log = _logGen.log('invalid-video', msg.assets) - } - - msg.assets.forEach(function (a) { - delete a.isVideo - }) - log.displayAdFound = msg.displayAdFound - log.requests = msg.requests - log.data = msg.event_data - - log.doc.finalPageUrl = log.doc.url - log.doc.url = exports.utils.normalizeUrl(msg.origUrl) - - _onAdFound(log) - } - - /** - * Add background listener. - * @param {String} event - * @param {Function} callback - */ - function addBackgroundListener(event, callback) { - chrome.runtime.onMessage.addListener(function (msg) { - if (msg.event === event) { - callback(msg) - } - }) - } - - exports.coordinator = { - /** - * @todo update description - */ - addPostMessageListener() { - if (!exports.utils.SCRIPT_IN_FRIENDLY_IFRAME) { - window.addEventListener('message', onPostMessage, false) - } - }, - - /** - * - * @param {*} sendFcn - * @param {*} origUrl - * - * @todo update description - */ - blockedRobotsMsgGen(sendFcn, origUrl) { - if (!origUrl.includes('google.com/_/chrome/newtab')) { - const onBlockedRobotsMessage = function () { - let log - log = _logGen.log('invalid-robotstxt', []) - log.doc.finalPageUrl = log.doc.url - log.doc.url = exports.utils.normalizeUrl(origUrl) - - sendFcn(log) - } - return onBlockedRobotsMessage - } else { - return function () {} - } - }, - - /** - * - * @param {*} onAdFound - */ - init(onAdFound) { - if (exports.utils.SCRIPT_IN_FRIENDLY_IFRAME) { - return false - } - - _onAdFound = onAdFound - if (exports.utils.SCRIPT_IN_WINDOW_TOP) { - const log = _logGen.log('page') - onAdFound(log) - - window.addEventListener('beforeunload', function (event) { - const log = _logGen.log('unload') - log.timing = window.performance.timing - onAdFound(log) - }) - - addBackgroundListener('new-video-ad', onVideoMessage) - addBackgroundListener('new-invalid-video-ad', onVideoMessage) - } - - exports.utils.onDocLoaded(document, extractAdsWrapper) - }, - } - })(exports) - - if (exports.utils.SCRIPT_IN_WINDOW_TOP) { - window.adparser = { - init: exports.coordinator.init, - addPostMessageListener: exports.coordinator.addPostMessageListener, - askIfTrackingEnabled: exports.utils.askIfTrackingEnabled, - blockedRobotsMsgGen: exports.coordinator.blockedRobotsMsgGen, - inWindowTop: exports.utils.SCRIPT_IN_WINDOW_TOP, - sendToBackground: exports.utils.sendToBackground, - } - } else { - exports.coordinator.addPostMessageListener() - exports.utils.askIfTrackingEnabled( - function () { - exports.coordinator.init(function () {}) - }, - function () {} - ) - } -})(window) -;(function (adparser, pageUrl) { - function onAdFound(log) { - adparser.sendToBackground( - { source: 'iframe.js', func: 'onAd', args: [log] }, - 'onAd', - '', - function () {} - ) - } - - if (adparser && adparser.inWindowTop) { - adparser.addPostMessageListener() - adparser.askIfTrackingEnabled(function () { - adparser.init(onAdFound) - }, adparser.blockedRobotsMsgGen(onAdFound, pageUrl)) - } -})(window.adparser, window.location.href) diff --git a/src/drivers/webextension/js/lib/network.js b/src/drivers/webextension/js/lib/network.js deleted file mode 100644 index 258d821ef..000000000 --- a/src/drivers/webextension/js/lib/network.js +++ /dev/null @@ -1,864 +0,0 @@ -'use strict' -;(function () { - const MIN_FF_MAJOR_VERSION = 51 - - let areListenersRegistered = false - const secBefore = 2000 - const secAfter = 5000 - const secBetweenDupAssets = 10e3 - const minVidSize = 500e3 - const maxVidSize = 25e6 - const maxContentRange = 25e6 - const videoExtensions = [ - 'af', - '3gp', - 'asf', - 'avchd', - 'avi', - 'cam', - 'dsh', - 'flv', - 'm1v', - 'm2v', - 'fla', - 'flr', - 'sol', - 'm4v', - 'mkv', - 'wrap', - 'mng', - 'mov', - 'mpeg', - 'mpg', - 'mpe', - 'mp4', - 'mxf', - 'nsv', - 'ogg', - 'rm', - 'svi', - 'smi', - 'wmv', - 'webm', - ] - const extensionsReg = new RegExp('\\.' + videoExtensions.join('$|\\.') + '$') - const videoContentTypesPrefixes = [ - 'binary/octet-stream', - 'video/', - 'flv-application/', - 'media', - ] - - const bannedContentTypes = ['video/mp2t', 'video/f4m', 'video/f4f'] - const bannedFiletypes = ['ts'] - const bannedFiletypesReg = new RegExp( - '\\.' + bannedFiletypes.join('$|\\.') + '$' - ) - const whitelistReqTypes = ['object', 'xmlhttprequest', 'other'] - - const topVideoAssetDomains = [ - '2mdn.net', - 'adap.tv', - 'adnxs.com', - 'adsrvr.org', - 'btrll.com', - 'celtra.com', - 'flashtalking.com', - 'flite.com', - 'innovid.com', - 'jivox.com', - 'mixpo.com', - 'nytimes.com', - 'playwire.com', - 'selectmedia.asia', - 'serving-sys.com', - 'solvemedia.com', - 'spotible.com', - 'teads.tv', - 'tribalfusion.com', - 'tubemogul.com', - 'videologygroup.com', - 'washingtonpost.com', - ] - - const robotsTxtAllows = Driver.checkRobots - if (!String.prototype.endsWith) { - String.prototype.endsWith = function (searchString, position) { - const subjectString = this.toString() - if ( - typeof position !== 'number' || - !isFinite(position) || - Math.floor(position) !== position || - position > subjectString.length - ) { - position = subjectString.length - } - position -= searchString.length - const lastIndex = subjectString.indexOf(searchString, position) - return lastIndex !== -1 && lastIndex === position - } - } - - function getFrame(getFrameDetails, callback) { - chrome.webNavigation.getFrame(getFrameDetails, callback) - } - - function ifTrackingEnabled(details, ifCallback, elseCallback) { - const fullIfCallback = function () { - allowedByRobotsTxt(details, ifCallback, elseCallback) - } - - Utils.getOption('tracking', true).then(function (tracking) { - if (tracking) { - fullIfCallback() - } else { - elseCallback() - } - }) - } - - function allowedByRobotsTxt(details, ifCallback, elseCallback) { - if (details.url && !details.url.startsWith('chrome://')) { - Driver.checkRobots(details.url, details.url.startsWith('https:')) - .then(ifCallback) - .catch(elseCallback) - } else { - elseCallback() - } - } - - function isPixelRequest(request) { - return ( - (request.type === 'image' || request.responseStatus === 204) && - request.size <= 1000 - ) - } - - function isVpaidOrVastRequest(request) { - const lowerCaseUrl = request.url.toLowerCase() - return lowerCaseUrl.includes('vpaid') || lowerCaseUrl.includes('vast') - } - - function hasValidRequestType(request) { - return whitelistReqTypes.includes(request.type) - } - - function stripQueryParams(url) { - return url.split('?', 1)[0] - } - - function parseHostnameFromUrl(url) { - try { - const { hostname } = new URL(url) - - return hostname - } catch { - return '' - } - } - - function hasDomain(url, domain) { - return parseHostnameFromUrl(url).endsWith(domain) - } - - function findHeader(headers, key) { - let header - for (let i = 0; i < headers.length; i += 1) { - header = headers[i] - if (header.name.toLowerCase() === key) { - return header - } - } - return null - } - - function validVideoType(vtype) { - const goodType = videoContentTypesPrefixes.some(function (prefix) { - return vtype.indexOf(prefix) === 0 - }) - return goodType - } - - function assetMsgKey(assetReq) { - const url = stripQueryParams(assetReq.url) - const key = assetReq.frameId + '-' + url - return key - } - - const PageNetworkTrafficCollector = function (tabId) { - this.tabId = tabId - this.displayAdFound = false - this.requests = {} - this.msgsBeingSent = {} - this.assetsSeen = {} - this.allRedirects = {} - } - - var globalPageContainer = { - collectors: {}, - dyingCollectors: {}, - - cleanupCollector(tabId) { - if (tabId in this.collectors) { - delete globalPageContainer.collectors[tabId] - } - }, - - onNewNavigation(details) { - const tabId = details.tabId - this.cleanupCollector(tabId) - - ifTrackingEnabled( - details, - function () { - if (!areListenersRegistered) { - registerListeners() - } - this.collectors[tabId] = new PageNetworkTrafficCollector(tabId) - }.bind(this), - function () { - if (areListenersRegistered) { - unregisterListeners() - } - } - ) - }, - - onNavigationCommitted(details) {}, - - onNavigationCompleted(details) {}, - - onTabClose(tabId, closeInfo) { - this.cleanupCollector(tabId) - delete this.collectors[tabId] - }, - - onDisplayAdFound(tabId) { - this.collectors[tabId].displayAdFound = true - }, - - getRandId() { - return String(Math.floor(Math.random() * 1e9)) - }, - - getCollector(tabId) { - if (this.collectors.hasOwnProperty(tabId)) { - return this.collectors[tabId] - } - return null - }, - - forwardCall(details, collectorMemberFunction) { - const collector = this.getCollector(details.tabId) - if (collector !== null) { - collectorMemberFunction.apply(collector, [details]) - } - }, - } - - PageNetworkTrafficCollector.prototype.sendLogMessageToTabConsole = function () { - const logMessage = Array.from(arguments).join(' ') - const message = { message: logMessage, event: 'console-log-message' } - chrome.tabs.sendMessage(this.tabId, message) - } - - PageNetworkTrafficCollector.prototype.sendToTab = function ( - assetReq, - reqs, - curPageUrl, - adTrackingEvent - ) { - const msg = {} - msg.assets = [] - msg.requests = [] - msg.event_data = {} - msg.event = adTrackingEvent - if (adTrackingEvent === 'new-video-ad') { - msg.requests = reqs - msg.requests.sort(function (reqA, reqB) { - return reqA.requestTimestamp - reqB.requestTimestamp - }) - if (assetReq) { - msg.assets = [assetReq] - } - } else if (adTrackingEvent === 'new-invalid-video-ad') { - msg.requests = reqs.map(function (request) { - return parseHostnameFromUrl(request.url) - }) - msg.assets = [ - { - url: parseHostnameFromUrl(assetReq.url), - - contentType: assetReq.contentType, - size: assetReq.size, - }, - ] - } - msg.origUrl = curPageUrl - msg.displayAdFound = this.displayAdFound - - chrome.tabs.sendMessage(this.tabId, msg) - } - - PageNetworkTrafficCollector.prototype.getRedirKey = function (url, frameId) { - return url + ':' + frameId - } - - PageNetworkTrafficCollector.prototype.seenBefore = function (request) { - const oldTime = this.assetsSeen[assetMsgKey(request)] - if (oldTime && request.requestTimestamp - oldTime < secBetweenDupAssets) { - return true - } - return false - } - - PageNetworkTrafficCollector.prototype.recordSeenAsset = function (request) { - this.assetsSeen[assetMsgKey(request)] = request.requestTimestamp - } - - PageNetworkTrafficCollector.prototype.onBeforeRequest = function (details) { - const req = { - url: details.url, - type: details.type, - httpMethod: details.method, - frameId: details.frameId, - parentFrameId: details.parentFrameId, - requestTimestamp: details.timeStamp, - } - this.requests[details.requestId] = req - } - - PageNetworkTrafficCollector.prototype.onSendHeaders = function (details) { - let request, header - request = this.requests[details.requestId] - header = request && findHeader(details.requestHeaders, 'x-requested-with') - if (header && header.value.toLowerCase().includes('flash')) { - request.from_flash = true - } - } - - PageNetworkTrafficCollector.prototype.onHeadersReceived = function (details) { - const getFrameDetails = { - tabId: details.tabId, - processId: null, - frameId: details.frameId, - } - const pageNetworkTrafficController = this - getFrame(getFrameDetails, function (frameDetails) { - if (frameDetails && frameDetails.url) { - pageNetworkTrafficController._onHeadersReceived(details, frameDetails) - } - }) - } - - PageNetworkTrafficCollector.prototype._onHeadersReceived = function ( - details, - frameDetails - ) { - let contentSize, contentRange - - const request = this.requests[details.requestId] - if (request) { - const redirParent = this.allRedirects[ - this.getRedirKey(details.url, details.frameId) - ] - let header = - request && findHeader(details.responseHeaders, 'content-type') - const contentType = header && header.value.toLowerCase() - - if (contentType) { - request.contentType = contentType - } - header = request && findHeader(details.responseHeaders, 'content-length') - contentSize = header && header.value - if (contentSize) { - request.size = request.size || 0 - request.size += parseInt(contentSize) - } - header = request && findHeader(details.responseHeaders, 'content-range') - contentRange = header && header.value - if (contentRange) { - request.contentRange = parseInt(contentRange.split('/')[1]) - } - - let frameUrl = null - if (frameDetails && frameDetails.url) { - frameUrl = frameDetails.url - } - if ( - !this.bannedRequest(request) && - (this.isVideoReq(frameUrl, request) || - (redirParent && redirParent.isVideo)) - ) { - request.isVideo = true - } - } - } - - PageNetworkTrafficCollector.prototype.onBeforeRedirect = function (details) { - const request = this.requests[details.requestId] - if (request) { - if (request.redirects) { - request.redirects.push(details.redirectUrl) - } else { - request.redirects = [details.redirectUrl] - } - this.allRedirects[ - this.getRedirKey(details.redirectUrl, details.frameId) - ] = request - } - } - - PageNetworkTrafficCollector.prototype.isYoutubeMastheadRequest = function ( - url - ) { - const re = /video_masthead/ - return this.hasYoutubeDomain(url) && re.test(url) - } - PageNetworkTrafficCollector.prototype.isYoutubeVideoRequest = function ( - srcUrl, - destUrl - ) { - if (!this.hasYoutubeDomain(srcUrl)) { - return false - } - - const re = /https?:\/\/r.*?\.googlevideo\.com\/videoplayback\?/ - return re.test(destUrl) - } - PageNetworkTrafficCollector.prototype.processResponse = function ( - requestDetails, - frameDetails - ) { - let request - if (requestDetails) { - request = this.requests[requestDetails.requestId] - if (request) { - request.responseStatus = requestDetails.statusCode - request.responseTimestamp = requestDetails.timeStamp - - let frameUrl = null - if (frameDetails && frameDetails.url) { - frameUrl = frameDetails.url - } - - let requestUrl = null - if (request.url) { - requestUrl = request.url - } - - if (this.isYoutubeAdReq(frameUrl, requestUrl)) { - const destVideoId = this.parseYoutubeVideoIdFromUrl(requestUrl) - const srcVideoId = this.parseYoutubeVideoIdFromUrl(frameUrl) - if (srcVideoId && destVideoId) { - request.isYoutubeAd = true - request.isVideo = true - request.rawSrcUrl = frameUrl - request.rawDestUrl = requestUrl - request.url = - 'https://www.youtube.com/watch?v=' + - this.parseYoutubeVideoIdFromUrl(requestUrl) - } - } else if ( - !this.bannedRequest(request) && - (this.isVideo || this.isVideoReq(frameUrl, request)) - ) { - request.isVideo = true - } - - if (request.isVideo) { - const msgKey = assetMsgKey(request) - this.msgsBeingSent[msgKey] = request - if (!this.seenBefore(request)) { - this.sendMsgWhenQuiet(msgKey) - } - this.recordSeenAsset(request) - } - } - } - } - - PageNetworkTrafficCollector.prototype.onResponseStarted = function ( - responseDetails - ) { - if (responseDetails.frameId < 0) { - responseDetails.frameId = 99999 - } - const getFrameDetails = { - tabId: responseDetails.tabId, - processId: null, - frameId: responseDetails.frameId, - } - const pageNetworkTrafficController = this - getFrame(getFrameDetails, function (frameDetails) { - if (frameDetails && frameDetails.url) { - pageNetworkTrafficController.processResponse( - responseDetails, - frameDetails - ) - } - }) - } - - PageNetworkTrafficCollector.prototype.hasBannedFiletype = function (request) { - const url = stripQueryParams(request.url) - if (bannedFiletypesReg.exec(url)) { - return true - } else { - return false - } - } - - PageNetworkTrafficCollector.prototype.checkContentHeaders = function ( - request - ) { - if (request.contentType && validVideoType(request.contentType)) { - return true - } - return false - } - - PageNetworkTrafficCollector.prototype.checkUrlExtension = function (request) { - const url = stripQueryParams(request.url) - if (extensionsReg.exec(url)) { - return true - } else { - return false - } - } - - PageNetworkTrafficCollector.prototype.isVideoReq = function ( - srcUrl, - request - ) { - if (this.isYoutubeVideoRequest(srcUrl, request.url)) { - return false - } - return this.checkUrlExtension(request) || this.checkContentHeaders(request) - } - PageNetworkTrafficCollector.prototype.hasYoutubeDomain = function (url) { - const hostname = parseHostnameFromUrl(url) - if (hostname === 'www.youtube.com') { - return true - } - return false - } - PageNetworkTrafficCollector.prototype.parseYoutubeVideoIdFromUrl = function ( - url - ) { - let re = /^https?:\/\/www\.youtube\.com\/get_video_info.*(?:\?|&)video_id=(.*?)(?:$|&)/ - let match = re.exec(url) - if (match && match.length > 1) { - return match[1] - } - - re = /^https?:\/\/www\.youtube\.com\/embed\/(.*?)(?:$|\?)/ - match = re.exec(url) - if (match && match.length > 1) { - return match[1] - } - - re = /^https?:\/\/www\.youtube\.com\/watch.*(\?|&)v=([^&]*)/ - match = re.exec(url) - if (match && match.length > 1) { - return match[1] - } - return null - } - - PageNetworkTrafficCollector.prototype.isYoutubeGetVideoInfoReq = function ( - url - ) { - const re = /^https?:\/\/www\.youtube\.com\/get_video_info\?/ - return re.test(url) - } - PageNetworkTrafficCollector.prototype.isYoutubeAdReq = function ( - srcUrl, - destUrl - ) { - if ( - !this.hasYoutubeDomain(srcUrl) || - !this.isYoutubeGetVideoInfoReq(destUrl) - ) { - return false - } - if ( - this.parseYoutubeVideoIdFromUrl(srcUrl) === - this.parseYoutubeVideoIdFromUrl(destUrl) && - !this.isYoutubeMastheadRequest(destUrl) - ) { - return false - } - return true - } - - PageNetworkTrafficCollector.prototype.bannedRequest = function (request) { - return ( - this.bannedVideoType(request) || - this.hasBannedFiletype(request) || - this.bannedVideoSize(request) - ) - } - - PageNetworkTrafficCollector.prototype.bannedVideoType = function (request) { - let badType = false - if (request.contentType) { - badType = bannedContentTypes.some(function (prefix) { - return request.contentType.includes(prefix) - }) - } - return badType - } - - PageNetworkTrafficCollector.prototype.bannedVideoSize = function (request) { - if (request.size !== null) { - if ( - request.size < minVidSize || - request.size > maxVidSize || - request.contentRange > maxContentRange - ) { - return true - } - } - return false - } - - PageNetworkTrafficCollector.prototype.grabTagReqs = function ( - tabRequests, - assetRequest - ) { - let minTimestamp, maxTimestamp - minTimestamp = assetRequest.requestTimestamp - secBefore - maxTimestamp = assetRequest.requestTimestamp + secAfter - - const filteredRequests = tabRequests.filter(function (request) { - return ( - request.requestTimestamp > minTimestamp && - request.requestTimestamp < maxTimestamp && - request.frameId === assetRequest.frameId && - request.url !== assetRequest.url && - (hasValidRequestType(request) || isPixelRequest(request)) - ) - }) - - return filteredRequests - } - - PageNetworkTrafficCollector.prototype.isValidVideoAd = function ( - assetRequest, - tagRequests - ) { - const hasVpaidOrVastRequest = tagRequests.some(function (tagRequest) { - return isVpaidOrVastRequest(tagRequest) - }) - - if (assetRequest.isYoutubeAd) { - return true - } - if (hasVpaidOrVastRequest) { - return true - } - const hasTopVideoAssetDomain = topVideoAssetDomains.some(function ( - assetDomain - ) { - return hasDomain(assetRequest.url, assetDomain) - }) - - return hasTopVideoAssetDomain - } - - PageNetworkTrafficCollector.prototype.sendMsgWhenQuiet = function (msgKey) { - const _this = this - let origPageUrl - let msgAssetReq - msgAssetReq = this.msgsBeingSent[msgKey] - chrome.tabs.get(this.tabId, function (tab) { - origPageUrl = tab.url - }) - - setTimeout(function () { - const rawRequests = [] - if (globalPageContainer.collectors[_this.tabId] === _this) { - for (const reqId in _this.requests) { - rawRequests.push(_this.requests[reqId]) - } - const tagReqs = _this.grabTagReqs(rawRequests, msgAssetReq) - - if (_this.isValidVideoAd(msgAssetReq, tagReqs)) { - _this.sendToTab(msgAssetReq, tagReqs, origPageUrl, 'new-video-ad') - } else { - _this.sendToTab( - msgAssetReq, - tagReqs, - origPageUrl, - 'new-invalid-video-ad' - ) - } - } else { - } - delete _this.msgsBeingSent[msgKey] - }, secAfter + secBefore) - } - - PageNetworkTrafficCollector.prototype.existingMessage = function ( - candidateRequest - ) { - const frameMsg = this.msgsBeingSent[candidateRequest.frameId] - if (frameMsg) { - return frameMsg - } else { - return null - } - } - - function onBeforeRequestListener(details) { - globalPageContainer.forwardCall( - details, - PageNetworkTrafficCollector.prototype.onBeforeRequest - ) - } - - function onSendHeadersListener(details) { - globalPageContainer.forwardCall( - details, - PageNetworkTrafficCollector.prototype.onSendHeaders - ) - } - - function onHeadersReceivedListener(details) { - globalPageContainer.forwardCall( - details, - PageNetworkTrafficCollector.prototype.onHeadersReceived - ) - } - - function onBeforeRedirectListener(details) { - globalPageContainer.forwardCall( - details, - PageNetworkTrafficCollector.prototype.onBeforeRedirect - ) - } - - function onResponseStartedListener(details) { - globalPageContainer.forwardCall( - details, - PageNetworkTrafficCollector.prototype.onResponseStarted - ) - } - - function onCommittedListener(details) { - if (details.frameId === 0) { - globalPageContainer.onNavigationCommitted(details) - } - } - - function onCompletedListener(details) { - if (details.frameId === 0) { - globalPageContainer.onNavigationCompleted(details) - } - } - - function onRemovedListener(tabId, closeInfo) { - globalPageContainer.onTabClose(tabId, closeInfo) - } - - function onMessageListener(message, sender, sendResponse) { - if (message.event === 'new-ad' && message.data.event === 'ad') { - const tabId = sender.tab.id - if (tabId) { - globalPageContainer.onDisplayAdFound(tabId) - } - } - } - - function registerListeners() { - chrome.webRequest.onBeforeRequest.addListener( - onBeforeRequestListener, - { urls: ['http://*/*', 'https://*/*'] }, - [] - ) - - chrome.webRequest.onSendHeaders.addListener( - onSendHeadersListener, - { urls: ['http://*/*', 'https://*/*'] }, - ['requestHeaders'] - ) - - chrome.webRequest.onHeadersReceived.addListener( - onHeadersReceivedListener, - { urls: ['http://*/*', 'https://*/*'] }, - ['responseHeaders'] - ) - - chrome.webRequest.onBeforeRedirect.addListener( - onBeforeRedirectListener, - { urls: ['http://*/*', 'https://*/*'] }, - [] - ) - - chrome.webRequest.onResponseStarted.addListener( - onResponseStartedListener, - { urls: ['http://*/*', 'https://*/*'] }, - ['responseHeaders'] - ) - - chrome.webNavigation.onCommitted.addListener(onCommittedListener) - chrome.webNavigation.onCompleted.addListener(onCompletedListener) - chrome.tabs.onRemoved.addListener(onRemovedListener) - chrome.runtime.onMessage.addListener(onMessageListener) - - areListenersRegistered = true - } - - function unregisterListeners() { - chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequestListener) - - chrome.webRequest.onSendHeaders.removeListener(onSendHeadersListener) - - chrome.webRequest.onHeadersReceived.removeListener( - onHeadersReceivedListener - ) - - chrome.webRequest.onBeforeRedirect.removeListener(onBeforeRedirectListener) - - chrome.webRequest.onResponseStarted.removeListener( - onResponseStartedListener - ) - - chrome.webNavigation.onCommitted.removeListener(onCommittedListener) - chrome.webNavigation.onCompleted.removeListener(onCompletedListener) - chrome.tabs.onRemoved.removeListener(onRemovedListener) - chrome.runtime.onMessage.removeListener(onMessageListener) - areListenersRegistered = false - } - - chrome.webNavigation.onBeforeNavigate.addListener( - function (details) { - if (details.frameId === 0) { - globalPageContainer.onNewNavigation(details) - } - }, - { - url: [{ urlMatches: 'http://*/*' }, { urlMatches: 'https://*/*' }], - } - ) - - chrome.runtime.onMessage.addListener((message, sender, callback) => { - if (message === 'is_tracking_enabled') { - ifTrackingEnabled( - sender.tab, - function () { - try { - callback({ tracking_enabled: true }) - } catch (err) {} - }, - function () { - try { - callback({ tracking_enabled: false }) - } catch (err) {} - } - ) - } - return true - }) -})() diff --git a/src/drivers/webextension/js/popup.js b/src/drivers/webextension/js/popup.js index bea241e7e..73f9bf5da 100644 --- a/src/drivers/webextension/js/popup.js +++ b/src/drivers/webextension/js/popup.js @@ -585,8 +585,12 @@ const Popup = { el.issue.classList.remove('issue--hidden') el.plusDownload.classList.remove('plus-download--hidden') - while (el.detections.firstChild) { - el.detections.removeChild(detections.firstChild) + let firstChild + + while ((firstChild = el.detections.firstChild)) { + if (firstChild instanceof Node) { + el.detections.removeChild(firstChild) + } } const pinnedCategory = await getOption('pinnedCategory') @@ -722,8 +726,12 @@ const Popup = { el.crawl.classList.add('plus-crawl--hidden') el.error.classList.add('plus-error--hidden') - while (el.panels.lastElementChild) { - el.panels.removeChild(el.panels.lastElementChild) + let lastChild + + while ((lastChild = el.panels.lastElementChild)) { + if (lastChild instanceof Node) { + el.panels.removeChild(lastChild) + } } try { diff --git a/src/drivers/webextension/manifest-v2.json b/src/drivers/webextension/manifest-v2.json index d4bdc9e79..055605b99 100644 --- a/src/drivers/webextension/manifest-v2.json +++ b/src/drivers/webextension/manifest-v2.json @@ -46,17 +46,6 @@ "js/content.js" ], "run_at": "document_idle" - }, - { - "matches": [ - "http://*/*", - "https://*/*" - ], - "js": [ - "js/lib/iframe.js" - ], - "run_at": "document_start", - "all_frames": true } ], "web_accessible_resources": [ diff --git a/src/drivers/webextension/manifest-v3.json b/src/drivers/webextension/manifest-v3.json index 39ca6ea92..94d48fbef 100644 --- a/src/drivers/webextension/manifest-v3.json +++ b/src/drivers/webextension/manifest-v3.json @@ -46,17 +46,6 @@ "js/content.js" ], "run_at": "document_idle" - }, - { - "matches": [ - "http://*/*", - "https://*/*" - ], - "js": [ - "js/lib/iframe.js" - ], - "run_at": "document_start", - "all_frames": true } ], "web_accessible_resources": [ diff --git a/src/technologies/a.json b/src/technologies/a.json index 27acf0ec1..02f563e4c 100644 --- a/src/technologies/a.json +++ b/src/technologies/a.json @@ -791,6 +791,22 @@ "scriptSrc": "\\.acquire\\.io/(?!cobrowse)", "website": "https://acquire.io" }, + "ActBlue": { + "cats": [ + 111 + ], + "description": "ActBlue is an online fundraising platform that facilitates secure donations to Democratic candidates and progressive causes, streamlining the process of processing and distributing campaign contributions.", + "icon": "ActBlue.svg", + "dom": "a[href*='//secure.actblue.com/donate/']", + "js": { + "actblue.__configuration": "" + }, + "pricing": [ + "payg" + ], + "saas": true, + "website": "https://secure.actblue.com" + }, "Act-On": { "cats": [ 32 @@ -2542,6 +2558,24 @@ ], "website": "https://www.alumniq.com/platform/" }, + "AlvandCMS": { + "cats": [ + 1 + ], + "description": "AlvandCMS is a PHP-based content management system that is commonly used in Iran.", + "icon": "AlvandCMS.png", + "implies": [ + "PHP", + "MySQL" + ], + "meta": { + "generator": "AlvandCMS\\s([\\d\\.]+)\\;version:\\1" + }, + "pricing": [ + "poa" + ], + "website": "https://alvandcms.ir" + }, "Amaya": { "cats": [ 20 @@ -3455,6 +3489,22 @@ "oss": true, "website": "https://apexcharts.com" }, + "ApexChat": { + "cats": [ + 52 + ], + "description": "ApexChat is a company that provides businesses with live chat software and services to facilitate real-time customer engagement, support, lead generation, and enhanced online interactions.", + "icon": "ApexChat.svg", + "js": { + "ApexChat": "", + "apexchat_dompopup_chatwindow_client": "" + }, + "pricing": [ + "poa" + ], + "saas": true, + "website": "https://www.apexchat.com" + }, "ApexPages": { "cats": [ 51 @@ -3489,10 +3539,7 @@ "icon": "Apisearch.png", "oss": true, "pricing": [ - "freemium", - "low", - "mid", - "recurring" + "payg" ], "saas": true, "scriptSrc": "static\\.apisearch\\.cloud", @@ -4514,6 +4561,26 @@ ], "website": "https://auth0.github.io/auth0.js/index.html" }, + "authorize.net": { + "cats": [ + 41 + ], + "description": "Authorize.net is a secure online payment gateway service that enables businesses to accept payments through various channels, such as ecommerce websites, mobile devices, and retail stores, providing a trusted platform for processing credit card and electronic cheque payments.", + "icon": "authorize.net.svg", + "headers": { + "Content-Security-Policy": "\\.authorize\\.net\\s" + }, + "js": { + "config.authorizenet_public_client_key": "" + }, + "scriptSrc": "\\.authorize\\.net/", + "saas": true, + "pricing": [ + "recurring", + "payg" + ], + "website": "https://www.authorize.net" + }, "Auth0 Lock": { "cats": [ 69 diff --git a/src/technologies/b.json b/src/technologies/b.json index 89ee643ea..0457b0162 100644 --- a/src/technologies/b.json +++ b/src/technologies/b.json @@ -465,6 +465,36 @@ "scriptSrc": "cdn\\.trybeans\\.com", "website": "https://www.trybeans.com/" }, + "Beehiiv": { + "cats": [ + 11 + ], + "description": "Beehiiv is a relatively young, hosted newsletter platform built for businesses and creators.", + "icon": "Beehiiv.svg", + "dom": "link[href*='media.beehiiv.com/']", + "pricing": [ + "freemium", + "low", + "recurring" + ], + "saas": true, + "website": "https://www.beehiiv.com" + }, + "Beehiiv RSS feed": { + "cats": [ + 49 + ], + "description": "Beehiiv RSS feed is a feature of the Beehiiv. Beehiiv is a relatively young, hosted newsletter platform built for businesses and creators.", + "icon": "Beehiiv.svg", + "dom": "iframe[src*='embeds.beehiiv.com/']", + "pricing": [ + "freemium", + "low", + "recurring" + ], + "saas": true, + "website": "https://www.beehiiv.com" + }, "Beeketing": { "cats": [ 32 @@ -528,20 +558,20 @@ "scriptSrc": "/shopify-apps//js/betterprice/betterprice\\.js", "website": "https://apps.shopify.com/better-price" }, - "Better Uptime": { + "Better Stack": { "cats": [ 13 ], - "description": "Better Uptime is the all-in-one infrastructure monitoring platform for your incident management, uptime monitoring, and status pages.", - "icon": "Better Uptime.svg", + "description": "Better Stack is the all-in-one infrastructure monitoring platform for your incident management, uptime monitoring, and status pages.", + "icon": "Better Stack.svg", "pricing": [ "freemium", "low", "recurring" ], "saas": true, - "scriptSrc": "//betteruptime\\.com/", - "website": "https://betterstack.com/better-uptime" + "scriptSrc": "//(?:uptime\\.)?(?:betteruptime|betterstack)\\.com/", + "website": "https://betterstack.com/uptime" }, "BetterDocs": { "cats": [ diff --git a/src/technologies/c.json b/src/technologies/c.json index 2b341c788..85d5c1b82 100644 --- a/src/technologies/c.json +++ b/src/technologies/c.json @@ -634,7 +634,7 @@ "meta": { "cargo_title": "" }, - "scriptSrc": "/cargo\\.", + "scriptSrc": "(?]+ alt=\"Powered by Mantis Bugtracker", "icon": "MantisBT.png", - "implies": "PHP", + "description": "MantisBT is an open-source, web-based issue tracking system written in PHP with a MySQL database backend, designed to facilitate bug tracking and project management for software development teams.", + "dom": "link[rel='stylesheet'][href*='/css/ace-mantis.css']", + "requires": "PHP", + "implies": "MySQL", + "oss": true, "website": "https://www.mantisbt.org" }, "ManyChat": { @@ -2231,17 +2249,6 @@ "scriptSrc": "cdn\\.moengage\\.\\w+", "website": "https://www.moengage.com" }, - "Moat": { - "cats": [ - 10 - ], - "description": "Moat is a digital ad analytics tool.", - "icon": "Moat.svg", - "scriptSrc": [ - "moatads\\.com" - ], - "website": "https://moat.com/" - }, "MobX": { "cats": [ 59 @@ -3378,4 +3385,4 @@ ], "website": "https://code.google.com/p/modwsgi" } -} \ No newline at end of file +} diff --git a/src/technologies/o.json b/src/technologies/o.json index 4451089f8..0cf1d332d 100644 --- a/src/technologies/o.json +++ b/src/technologies/o.json @@ -436,6 +436,7 @@ "cpe": "cpe:2.3:a:omeka:omeka:*:*:*:*:*:*:*:*:*", "description": "Omeka is a free Content Management System (CMS) used by archives, historical societies, libraries, museums, and individual researchers for publishing digital collections.", "icon": "Omeka.png", + "dom": "link[rel*='stylesheet'][href*='css/myomeka.css'], link[rel*='stylesheet'][href*='/omeka/plugins/'], footer > p > a[href*='//omeka.org']", "js": { "Omeka": "" }, @@ -1501,6 +1502,17 @@ "scriptSrc": "service\\.maxymiser\\.net", "website": "https://www.oracle.com/uk/cx/marketing/personalization-testing" }, + "Oracle Moat Measurement": { + "cats": [ + 10 + ], + "description": "Oracle Moat delivers solutions that are critical to measuring advertising effectiveness, including verification and attention, reach, and frequency as well as sales lift measurement.", + "icon": "Oracle.svg", + "scriptSrc": [ + "moatads\\.com" + ], + "website": "https://www.oracle.com/cx/advertising/measurement/" + }, "Oracle Recommendations On Demand": { "cats": [ 10 @@ -1606,6 +1618,24 @@ "saas": true, "website": "https://www.orckestra.com" }, + "OrderCast": { + "cats": [ + 6 + ], + "description": "OrderCast is a B2B ecommerce platform focused on streamlining wholesale operations, offering SKU management, order handling, and customisable online store features for improved customer experience.", + "icon": "OrderCast.svg", + "implies": [ + "Python", + "React" + ], + "pricing": [ + "poa", + "recurring" + ], + "saas": true, + "url": "\\.(?:eu|us1)\\.ordercast\\.io/", + "website": "https://www.ordercast.io" + }, "Order Deadline": { "cats": [ 100 @@ -1954,4 +1984,4 @@ }, "website": "https://owncloud.org" } -} \ No newline at end of file +} diff --git a/src/technologies/p.json b/src/technologies/p.json index 6e2cbcb1f..eb2a72db2 100644 --- a/src/technologies/p.json +++ b/src/technologies/p.json @@ -2739,10 +2739,10 @@ "MySQL" ], "js": { - "freeProductTranslation": "\\;confidence:25", + "freeProductTranslation": "\\;confidence:40", "prestashop": "", - "priceDisplayMethod": "\\;confidence:25", - "priceDisplayPrecision": "\\;confidence:25", + "priceDisplayMethod": "\\;confidence:40", + "priceDisplayPrecision": "\\;confidence:40", "rcAnalyticsEvents.eventPrestashopCheckout": "" }, "meta": { @@ -3045,6 +3045,33 @@ "saas": true, "website": "https://www.sitefinity.com" }, + "Progress WS_FTP": { + "cats": [ + 19 + ], + "cpe":"cpe:2.3:a:progress:ws_ftp_server:*:*:*:*:*:*:*:*", + "description": "Progress WS_FTP is a file transfer client software developed by Progress Software Corporation, supporting FTP, FTPS, SFTP, and HTTPS protocols with features like drag-and-drop support, file synchronization, and encrypted data transmission.", + "dom": "form[name='formLogin'][action='login.aspx' i][id='formLogin']\\;confidence:40", + "scriptSrc": "/ThinClient/(?:WTM|WebResource)(?:\\.axd|/public)\\;confidence:60", + "requires": "Microsoft ASP.NET", + "icon": "Progress.svg", + "website": "https://www.progress.com/ws_ftp" + }, + "Progress MOVEit": { + "cats": [ + 19 + ], + "description": "Progress MOVEit is a managed file transfer solution that enables secure and compliant transfer of sensitive files while providing automation, central management, and auditing capabilities.", + "headers": { + "X-Moveitisapi-Version": "^(.+)$\\;version:\\1" + }, + "meta": { + "apple-itunes-app": "app-id=1500056420", + "google-play-app": "app-id=com\\.progress\\.moveit\\.transfer\\.dev\\.appid" + }, + "icon": "Progress.svg", + "website": "https://www.progress.com/moveit" + }, "Project Wonderful": { "cats": [ 36 diff --git a/src/technologies/r.json b/src/technologies/r.json index af5bfc191..affa3532d 100644 --- a/src/technologies/r.json +++ b/src/technologies/r.json @@ -2310,6 +2310,22 @@ ], "website": "https://www.royalmail.com" }, + "Rubedo": { + "cats": [ + 1 + ], + "description": "Rubedo is an open-source PHP CMS powered by the Zend Framework, NoSQL MongoDB, Elasticsearch, and AngularJS, offering advanced features for content management and development.", + "implies": [ + "MongoDB", + "Elasticsearch", + "PHP" + ], + "js": { + "rubedoConfig": "" + }, + "oss": true, + "website": "https://github.com/WebTales/rubedo" + }, "Rubicon Project": { "cats": [ 36 diff --git a/src/technologies/s.json b/src/technologies/s.json index d0d4022af..daa565a6b 100644 --- a/src/technologies/s.json +++ b/src/technologies/s.json @@ -793,12 +793,21 @@ "cats": [ 6 ], - "description": "Salla is an ecommerce platform.", + "description": "Salla is an ecommerce platform specifically tailored to serve businesses and customers in Saudi Arabia.", "headers": { "x-powered-by": "^Salla$" }, "icon": "Salla.svg", - "scriptSrc": "cdn\\.salla\\.sa/", + "js": { + "Salla.shop": "", + "SallaApplePay": "" + }, + "pricing": [ + "freemium", + "low", + "recurring" + ], + "saas": true, "website": "https://salla.sa" }, "Salonist": { @@ -1531,6 +1540,20 @@ "saas": true, "website": "https://selldone.com" }, + "SellersCommerce": { + "cats": [ + 6 + ], + "description": "SellersCommerce is a medium ecommerce software company that provides b2b ecommerce platform to retail companies.", + "icon": "SellersCommerce.png", + "pricing": [ + "poa" + ], + "saas": true, + "scriptSrc": "\\.sellerscommerce\\.com/", + "requires": "Microsoft ASP.NET", + "website": "https://www.sellerscommerce.com" + }, "Sellfy": { "cats": [ 6 @@ -2752,6 +2775,25 @@ "scriptSrc": "cdn\\.shoppinggives\\.com/", "website": "https://shoppinggives.com" }, + "Shoppub": { + "cats": [ + 6 + ], + "description": "Shoppub is an ecommerce platform that focuses on implementing advanced business rules.", + "icon": "Shoppub.svg", + "meta": { + "author": "^Shoppub$" + }, + "js": { + "Shoppub.store": "" + }, + "pricing": [ + "mid", + "recurring" + ], + "saas": true, + "website": "https://www.shoppub.com.br" + }, "Shoppy": { "cats": [ 6 @@ -3826,6 +3868,23 @@ "saas": true, "website": "https://www.skolengo.com" }, + "Skyflow": { + "cats": [ + 16 + ], + "description": "Skyflow is a company that provides a secure data privacy platform and API.", + "icon": "Skyflow.svg", + "js": { + "Skyflow.ElementType.CARD_NUMBER": "", + "Skyflow.ValidationRuleType": "" + }, + "pricing": [ + "high", + "recurring" + ], + "saas": true, + "website": "https://www.skyflow.com" + }, "Sky-Shop": { "cats": [ 6 @@ -4631,7 +4690,7 @@ "SolidJS" ], "js": { - "_$HY": "" + "_$HY.init": "" }, "oss": true, "website": "https://start.solidjs.com" @@ -4929,7 +4988,7 @@ 67 ], "description": "Spatie Laravel Cookie Consent is a banner that is displayed on websites to ask visitors for consent for the use of cookies.", - "icon": "Spatie.png", + "icon": "Spatie.svg", "implies": "Laravel", "js": { "laravelCookieConsent": "" @@ -4959,7 +5018,7 @@ ], "description": "Spatie Support Bubble is a non-intrusive support form.", "dom": "div.spatie-support-bubble", - "icon": "Spatie.png", + "icon": "Spatie.svg", "implies": [ "Laravel", "Tailwind CSS" @@ -6227,7 +6286,7 @@ 1 ], "description": "Storyblok is a headless CMS with a visual editor for developers, marketers and content editors. Storyblok helps your team to manage content and digital experiences for every use-case from corporate websites, ecommerce, helpdesks, mobile apps, screen displays, and more.", - "dom": "img[src*='//a.storyblok.com/']", + "dom": "img[src*='//a.storyblok.com/'], img[srcset*='a.storyblok.com']", "headers": { "content-security-policy": "app\\.storyblok\\.com", "x-frame-options": "app\\.storyblok\\.com" @@ -7210,4 +7269,4 @@ }, "website": "https://styled-components.com" } -} \ No newline at end of file +} diff --git a/src/technologies/t.json b/src/technologies/t.json index 0d973b9ec..276dc8f2c 100644 --- a/src/technologies/t.json +++ b/src/technologies/t.json @@ -2036,6 +2036,20 @@ "saas": true, "website": "https://www.tiendanube.com" }, + "Tiiny Host": { + "cats": [ + 62 + ], + "description": "Tiiny Host is a web hosting service for static sites with support for custom domains, SSL, password protection, and built-in analytics.", + "icon": "Tiiny Host.png", + "pricing": [ + "low", + "recurring", + "freemium" + ], + "scriptSrc": "(?://|\\.)tiiny\\.(?:host|site)/", + "website": "https://tiiny.host" + }, "TikTok Pixel": { "cats": [ 10 diff --git a/src/technologies/w.json b/src/technologies/w.json index 5edeba5bc..85591cee6 100644 --- a/src/technologies/w.json +++ b/src/technologies/w.json @@ -620,6 +620,16 @@ "requires": "WordPress", "website": "https://music.flatfull.com/waveme/about/" }, + "WayForPay": { + "cats": [ + 41 + ], + "description": "WayForPay is a payment processing service provider based in Europe.", + "dom": "form[action*='secure.wayforpay.com']", + "icon": "WayForPay.svg", + "scriptSrc": "secure\\.wayforpay\\.com", + "website": "https://wayforpay.com" + }, "Wazimo": { "cats": [ 36 @@ -2392,4 +2402,4 @@ "url": "^https?://[^/]+\\.wpcache\\.co", "website": "https://wpcache.co" } -} \ No newline at end of file +} diff --git a/src/wappalyzer.js b/src/wappalyzer.js index 3ce82e235..f99a592f2 100644 --- a/src/wappalyzer.js +++ b/src/wappalyzer.js @@ -99,7 +99,7 @@ const Wappalyzer = { const resolved = detections.reduce((resolved, { technology, lastUrl }) => { if ( resolved.findIndex( - ({ technology: { name } }) => name === technology.name + ({ technology: { name } }) => name === technology?.name ) === -1 ) { let version = '' diff --git a/yarn.lock b/yarn.lock index aee5273c5..5fc0ac563 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2075,19 +2075,19 @@ schema-utils@^2.6.5: ajv-keywords "^3.5.2" "semver@2 || 3 || 4 || 5", semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.2.1, semver@^7.3.2: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0"