diff --git a/.editorconfig b/.editorconfig index 92b1c978ec6e3169cbbb11b4baebdf9cced1301f..0f1786729b408ab2c4473567fcb202acfeb10bd5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,14 +1,9 @@ root = true [*] -indent_style = tab +charset = utf-8 end_of_line = lf +indent_size = 2 +indent_style = space insert_final_newline = true trim_trailing_whitespace = true - -[*.{js,py}] -charset = utf-8 - -[*.py] -indent_style = space -indent_size = 4 diff --git a/Dockerfile b/Dockerfile index e10b845322f7d456c165bd9427a32a104161b653..9c78eefe985b40a195f5823972eed8a6f5af1b9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,16 @@ FROM alpine MAINTAINER Elbert Alias +EXPOSE 8080 + ENV WAPPALYZER_DIR=/opt/wappalyzer -RUN apk add --no-cache \ +RUN apk update && apk add --no-cache \ bash \ curl \ fontconfig \ nodejs \ + nodejs-npm \ optipng \ zip @@ -23,7 +26,9 @@ RUN apk del \ RUN npm i -g \ jsonlint-cli \ manifoldjs \ - svg2png-many + svg2png-many \ + webpack \ + yarn RUN mkdir -p $WAPPALYZER_DIR diff --git a/bin/dev b/bin/dev new file mode 100755 index 0000000000000000000000000000000000000000..1b58c63d77a28d50e886b084e3034783e3318539 --- /dev/null +++ b/bin/dev @@ -0,0 +1,5 @@ +#!/bin/bash + +cd "$(dirname $0)/.." + +yarn run dev diff --git a/bin/node_modules/.yarn-integrity b/bin/node_modules/.yarn-integrity new file mode 100644 index 0000000000000000000000000000000000000000..eb85016bac51e6daad299d02a12c1f90b07d7674 --- /dev/null +++ b/bin/node_modules/.yarn-integrity @@ -0,0 +1,8 @@ +{ + "flags": [], + "linkedModules": [], + "topLevelPatters": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/bin/run b/bin/run index f7a29c820384b3a338d84e4b92a0b47d412bdbef..4fcaccd0c44900b5008fb8fb709f3c7b4688f307 100755 --- a/bin/run +++ b/bin/run @@ -3,6 +3,20 @@ cd "$(dirname $0)" if [[ -f "$1" ]]; then + yarn install + + pushd ../src/drivers/webextension + + yarn install + + popd + + pushd ../src/drivers/npm + + yarn install + + popd + ./$1 $@ else ./help diff --git a/bin/yarn.lock b/bin/yarn.lock new file mode 100644 index 0000000000000000000000000000000000000000..fb57ccd13afbd082ad82051c2ffebef4840661ec --- /dev/null +++ b/bin/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + diff --git a/content.js b/content.js new file mode 100644 index 0000000000000000000000000000000000000000..530856a613148f4141e7c9b7ec6c90762dfe94d5 --- /dev/null +++ b/content.js @@ -0,0 +1,147 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +/** global: browser */ + +(function () { + var c = { + init: function () { + var html = document.documentElement.outerHTML; + + c.log('Function call: init()'); + + if (html.length > 50000) { + html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length); + } + + browser.runtime.sendMessage({ + id: 'analyze', + subject: { html: html }, + source: 'content.js' + }); + + c.getEnvironmentVars(); + }, + + log: function (message) { + browser.runtime.sendMessage({ + id: 'log', + message: message, + source: 'content.js' + }); + }, + + getEnvironmentVars: function () { + var container, script; + + c.log('Function call: getEnvironmentVars()'); + + if (typeof document.body === 'undefined') { + return; + } + + try { + container = document.createElement('wappalyzerData'); + + container.setAttribute('id', 'wappalyzerData'); + container.setAttribute('style', 'display: none'); + + script = document.createElement('script'); + + script.setAttribute('id', 'wappalyzerEnvDetection'); + script.setAttribute('src', browser.extension.getURL('js/inject.js')); + + container.addEventListener('wappalyzerEvent', function (event) { + var environmentVars = event.target.childNodes[0].nodeValue; + + document.documentElement.removeChild(container); + document.documentElement.removeChild(script); + + environmentVars = environmentVars.split(' ').slice(0, 500); + + browser.runtime.sendMessage({ + id: 'analyze', + subject: { env: environmentVars }, + source: 'content.js' + }); + }, true); + + document.documentElement.appendChild(container); + document.documentElement.appendChild(script); + } catch (e) { + c.log('Error: ' + e); + } + } + }; + + c.init(); +})(); + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/driver.js b/driver.js new file mode 100644 index 0000000000000000000000000000000000000000..5e0d608080b5cd32d61f8b9391ad5b0ad30fdce0 --- /dev/null +++ b/driver.js @@ -0,0 +1,1853 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || Function("return this")() || (1,eval)("this"); +} catch(e) { + // This works if the window reference is available + if(typeof window === "object") + g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global) {/*global window, global*/ +var util = __webpack_require__(2) +var assert = __webpack_require__(7) +var now = __webpack_require__(8) + +var slice = Array.prototype.slice +var console +var times = {} + +if (typeof global !== "undefined" && global.console) { + console = global.console +} else if (typeof window !== "undefined" && window.console) { + console = window.console +} else { + console = {} +} + +var functions = [ + [log, "log"], + [info, "info"], + [warn, "warn"], + [error, "error"], + [time, "time"], + [timeEnd, "timeEnd"], + [trace, "trace"], + [dir, "dir"], + [consoleAssert, "assert"] +] + +for (var i = 0; i < functions.length; i++) { + var tuple = functions[i] + var f = tuple[0] + var name = tuple[1] + + if (!console[name]) { + console[name] = f + } +} + +module.exports = console + +function log() {} + +function info() { + console.log.apply(console, arguments) +} + +function warn() { + console.log.apply(console, arguments) +} + +function error() { + console.warn.apply(console, arguments) +} + +function time(label) { + times[label] = now() +} + +function timeEnd(label) { + var time = times[label] + if (!time) { + throw new Error("No such label: " + label) + } + + var duration = now() - time + console.log(label + ": " + duration + "ms") +} + +function trace() { + var err = new Error() + err.name = "Trace" + err.message = util.format.apply(null, arguments) + console.error(err.stack) +} + +function dir(object) { + console.log(util.inspect(object) + "\n") +} + +function consoleAssert(expression) { + if (!expression) { + var arr = slice.call(arguments, 1) + assert.ok(false, util.format.apply(null, arr)) + } +} + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(0))) + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global, process, console) {// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = __webpack_require__(5); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = __webpack_require__(6); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(0), __webpack_require__(4), __webpack_require__(1))) + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(console) {/** + * WebExtension driver + */ + +/** global: browser */ +/** global: chrome */ +/** global: wappalyzer */ +/** global: XMLHttpRequest */ + +(function () { + if (wappalyzer == null) { + return; + } + + var w = wappalyzer, + firstRun = false, + upgraded = false, + tab, + tabCache = {}, + headersCache = {}; + + w.driver = { + timeout: 1000, + + /** + * Log messages to console + */ + log: function (args) { + console.log('[wappalyzer ' + args.type + ']', '[' + args.source + ']', JSON.parse(args.message)); + }, + + /** + * Get a value from localStorage + */ + getOption: function (name, defaultValue, callback) { + var func = function (item) { + callback(item.hasOwnProperty(name) ? item[name] : defaultValue); + }; + + try { + // Chrome, Firefox + browser.storage.local.get(name).then(func); + } catch (e) { + // Edge + browser.storage.local.get(name, func); + } + }, + + /** + * Set a value in localStorage + */ + setOption: function (name, value) { + var option = {}; + + option[name] = value; + + browser.storage.local.set(option); + }, + + /** + * Initialize + */ + init: function () { + w.log('Function call: w.driver.init()', 'driver'); + + // Load apps.json + var xhr = new XMLHttpRequest(); + + xhr.open('GET', 'apps.json', true); + + xhr.overrideMimeType('application/json'); + + xhr.onload = function () { + var json = JSON.parse(xhr.responseText); + + w.categories = json.categories; + w.apps = json.apps; + + w.driver.categoryOrder = Object.keys(w.categories).sort(function (a, b) { + return w.categories[a].priority - w.categories[b].priority; + }); + }; + + xhr.send(null); + + // Version check + try { + var version = browser.runtime.getManifest().version; + + w.driver.getOption('version', null, function (previousVersion) { + if (previousVersion === null) { + w.driver.goToURL({ + url: w.config.websiteURL + 'installed' + }); + } else if (version !== previousVersion) { + w.driver.getOption('upgradeMessage', true, function (upgradeMessage) { + if (upgradeMessage) { + w.driver.goToURL({ + url: w.config.websiteURL + 'upgraded', + background: true + }); + } + }); + } + + w.driver.setOption('version', version); + }); + } catch (e) { + // Do nothing + } + + (chrome || browser).runtime.onMessage.addListener(w.driver.onMessage); + + var callback = function (tabs) { + tabs.forEach(function (tab) { + if (tab.url.match(/^https?:\/\//)) { + browser.tabs.executeScript(tab.id, { file: 'js/content.js' }); + } + }); + }; + + try { + browser.tabs.query({}).then(callback); + } catch (e) { + browser.tabs.query({}, callback); + } + + browser.tabs.onRemoved.addListener(function (tabId) { + tabCache[tabId] = null; + }); + + // Live intercept headers using webRequest API + browser.webRequest.onCompleted.addListener(function (details) { + var responseHeaders = {}; + + if (details.responseHeaders) { + var uri = details.url.replace(/#.*$/, ''); // Remove hash + + details.responseHeaders.forEach(function (header) { + responseHeaders[header.name.toLowerCase()] = header.value || '' + header.binaryValue; + }); + + if (headersCache.length > 50) { + headersCache = {}; + } + + if (/text\/html/.test(responseHeaders['content-type'])) { + if (headersCache[uri] === undefined) { + headersCache[uri] = {}; + } + + for (var header in responseHeaders) { + if (responseHeaders.hasOwnProperty(header)) { + headersCache[uri][header] = responseHeaders[header]; + } + } + } + } + }, { urls: ['http://*/*', 'https://*/*'], types: ['main_frame'] }, ['responseHeaders']); + + if (firstRun) { + w.driver.goToURL({ url: w.config.websiteURL + 'installed', medium: 'install' }); + + firstRun = false; + } + + if (upgraded) { + w.driver.goToURL({ url: w.config.websiteURL + 'upgraded', medium: 'upgrade', background: true }); + + upgraded = false; + } + }, + + onMessage: function (message, sender, sendResponse) { + var hostname, + response, + a = document.createElement('a'); + + if (typeof message.id != 'undefined') { + if (message.id !== 'log') { + w.log('Message received from ' + message.source + ': ' + message.id, 'driver'); + } + + switch (message.id) { + case 'log': + w.log(message.message, message.source); + + break; + case 'analyze': + tab = sender.tab; + + a.href = tab.url.replace(/#.*$/, ''); + + hostname = a.hostname; + + if (headersCache[a.href] !== undefined) { + message.subject.headers = headersCache[a.href]; + } + + w.analyze(hostname, a.href, message.subject); + + break; + case 'ad_log': + w.adCache.push(message.subject); + w.checkAdCache(); + break; + case 'get_apps': + response = { + tabCache: tabCache[message.tab.id], + apps: w.apps, + categories: w.categories + }; + + break; + default: + } + + sendResponse(response); + } + }, + + /** + * Open a tab + */ + goToURL: function (args) { + browser.tabs.create({ + url: args.url, + active: args.background === undefined || !args.background + }); + }, + + /** + * Display apps + */ + displayApps: function () { + var url = tab.url.replace(/#.*$/, ''), + count = w.detected[url] ? Object.keys(w.detected[url]).length.toString() : '0'; + + if (tabCache[tab.id] == null) { + tabCache[tab.id] = { + count: 0, + appsDetected: [] + }; + } + + tabCache[tab.id].count = count; + tabCache[tab.id].appsDetected = w.detected[url]; + + if (count > 0) { + w.driver.getOption('dynamicIcon', true, function (dynamicIcon) { + var appName, + found = false; + + // Find the main application to display + w.driver.categoryOrder.forEach(function (match) { + for (appName in w.detected[url]) { + w.apps[appName].cats.forEach(function (cat) { + var icon = w.apps[appName].icon || 'default.svg'; + + if (!dynamicIcon) { + icon = 'default.svg'; + } + + if (cat === match && !found) { + if (/\.svg$/i.test(icon)) { + icon = 'converted/' + icon.replace(/\.svg$/, '.png'); + } + + browser.pageAction.setIcon({ + tabId: tab.id, + path: 'images/icons/' + icon + }); + + found = true; + } + }); + } + }); + + if (typeof chrome !== 'undefined') { + // Browser polyfill doesn't seem to work here + chrome.pageAction.show(tab.id); + } else { + browser.pageAction.show(tab.id); + } + }); + }; + }, + + /** + * Anonymously track detected applications for research purposes + */ + ping: function () { + w.driver.getOption('tracking', true, function (tracking) { + if (Object.keys(w.ping.hostnames).length && tracking) { + w.driver.post('http://ping.wappalyzer.com/v2/', w.ping); + + w.ping = { hostnames: {} }; + + w.driver.post('https://ad.wappalyzer.com/log/wp/', w.adCache); + + w.adCache = []; + } + }); + }, + + /** + * Make POST request + */ + post: function (url, data) { + var xhr = new XMLHttpRequest(); + + xhr.open('POST', url, true); + + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + w.log({ 'POST request': { url: url, status: xhr.status, data: data } }, 'driver'); + } + }; + + xhr.send('json=' + encodeURIComponent(JSON.stringify(data))); + } + }; + + w.init(); +})(); +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1))) + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} + +/***/ }), +/* 6 */ +/***/ (function(module, exports) { + +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { + +// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js +// original notice: + +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +function compare(a, b) { + if (a === b) { + return 0; + } + + var x = a.length; + var y = b.length; + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + + if (x < y) { + return -1; + } + if (y < x) { + return 1; + } + return 0; +} +function isBuffer(b) { + if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { + return global.Buffer.isBuffer(b); + } + return !!(b != null && b._isBuffer); +} + +// based on node assert, original notice: + +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +var util = __webpack_require__(2); +var hasOwn = Object.prototype.hasOwnProperty; +var pSlice = Array.prototype.slice; +var functionsHaveNames = (function () { + return function foo() {}.name === 'foo'; +}()); +function pToString (obj) { + return Object.prototype.toString.call(obj); +} +function isView(arrbuf) { + if (isBuffer(arrbuf)) { + return false; + } + if (typeof global.ArrayBuffer !== 'function') { + return false; + } + if (typeof ArrayBuffer.isView === 'function') { + return ArrayBuffer.isView(arrbuf); + } + if (!arrbuf) { + return false; + } + if (arrbuf instanceof DataView) { + return true; + } + if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { + return true; + } + return false; +} +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +var regex = /\s*function\s+([^\(\s]*)\s*/; +// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js +function getName(func) { + if (!util.isFunction(func)) { + return; + } + if (functionsHaveNames) { + return func.name; + } + var str = func.toString(); + var match = str.match(regex); + return match && match[1]; +} +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = getName(stackStartFunction); + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function truncate(s, n) { + if (typeof s === 'string') { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} +function inspect(something) { + if (functionsHaveNames || !util.isFunction(something)) { + return util.inspect(something); + } + var rawname = getName(something); + var name = rawname ? ': ' + rawname : ''; + return '[Function' + name + ']'; +} +function getMessage(self) { + return truncate(inspect(self.actual), 128) + ' ' + + self.operator + ' ' + + truncate(inspect(self.expected), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); + } +}; + +function _deepEqual(actual, expected, strict, memos) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + } else if (isBuffer(actual) && isBuffer(expected)) { + return compare(actual, expected) === 0; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if ((actual === null || typeof actual !== 'object') && + (expected === null || typeof expected !== 'object')) { + return strict ? actual === expected : actual == expected; + + // If both values are instances of typed arrays, wrap their underlying + // ArrayBuffers in a Buffer each to increase performance + // This optimization requires the arrays to have the same type as checked by + // Object.prototype.toString (aka pToString). Never perform binary + // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their + // bit patterns are not identical. + } else if (isView(actual) && isView(expected) && + pToString(actual) === pToString(expected) && + !(actual instanceof Float32Array || + actual instanceof Float64Array)) { + return compare(new Uint8Array(actual.buffer), + new Uint8Array(expected.buffer)) === 0; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else if (isBuffer(actual) !== isBuffer(expected)) { + return false; + } else { + memos = memos || {actual: [], expected: []}; + + var actualIndex = memos.actual.indexOf(actual); + if (actualIndex !== -1) { + if (actualIndex === memos.expected.indexOf(expected)) { + return true; + } + } + + memos.actual.push(actual); + memos.expected.push(expected); + + return objEquiv(actual, expected, strict, memos); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b, strict, actualVisitedObjects) { + if (a === null || a === undefined || b === null || b === undefined) + return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) + return a === b; + if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) + return false; + var aIsArgs = isArguments(a); + var bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b, strict); + } + var ka = objectKeys(a); + var kb = objectKeys(b); + var key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length !== kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] !== kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +assert.notDeepStrictEqual = notDeepStrictEqual; +function notDeepStrictEqual(actual, expected, message) { + if (_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); + } +} + + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } + + try { + if (actual instanceof expected) { + return true; + } + } catch (e) { + // Ignore. The instanceof check doesn't work for arrow functions. + } + + if (Error.isPrototypeOf(expected)) { + return false; + } + + return expected.call({}, actual) === true; +} + +function _tryBlock(block) { + var error; + try { + block(); + } catch (e) { + error = e; + } + return error; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (typeof block !== 'function') { + throw new TypeError('"block" argument must be a function'); + } + + if (typeof expected === 'string') { + message = expected; + expected = null; + } + + actual = _tryBlock(block); + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + var userProvidedMessage = typeof message === 'string'; + var isUnwantedException = !shouldThrow && util.isError(actual); + var isUnexpectedException = !shouldThrow && actual && !expected; + + if ((isUnwantedException && + userProvidedMessage && + expectedException(actual, expected)) || + isUnexpectedException) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws(true, block, error, message); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws(false, block, error, message); +}; + +assert.ifError = function(err) { if (err) throw err; }; + +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; +}; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(0))) + +/***/ }), +/* 8 */ +/***/ (function(module, exports) { + +module.exports = now + +function now() { + return new Date().getTime() +} + + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/options.js b/options.js new file mode 100644 index 0000000000000000000000000000000000000000..6bfdc3d20f37720d50c1a71b385e1524880c8632 --- /dev/null +++ b/options.js @@ -0,0 +1,144 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +/** global: browser */ +/** global: wappalyzer */ + +document.addEventListener('DOMContentLoaded', function () { + var d = document; + + var options = { + init: function () { + options.load(); + + d.querySelector('#github').addEventListener('click', function () { + open(wappalyzer.config.githubURL); + }); + + d.querySelector('#twitter').addEventListener('click', function () { + open(wappalyzer.config.twitterURL); + }); + + d.querySelector('#wappalyzer').addEventListener('click', function () { + open(wappalyzer.config.websiteURL); + }); + }, + + get: function (name, defaultValue, callback) { + browser.storage.local.get(name).then(function (item) { + callback(item.hasOwnProperty(name) ? item[name] : defaultValue); + }); + }, + + set: function (name, value) { + var option = {}; + + option[name] = value; + + browser.storage.local.set(option); + }, + + load: function () { + options.get('upgradeMessage', true, function (value) { + var el = d.querySelector('#option-upgrade-message'); + + el.checked = value; + + el.addEventListener('change', function () { + options.set('upgradeMessage', el.checked); + }); + }); + + options.get('dynamicIcon', true, function (value) { + var el = d.querySelector('#option-dynamic-icon'); + + el.checked = value; + + el.addEventListener('change', function () { + options.set('dynamicIcon', el.checked); + }); + }); + + options.get('tracking', true, function (value) { + var el = d.querySelector('#option-tracking'); + + el.checked = value; + + el.addEventListener('change', function () { + options.set('tracking', el.checked); + }); + }); + } + }; + + options.init(); +}); + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/package.json b/package.json index dd2dbe93dc8b36fac79c6973753eb735e59bdb16..b0fbe1ae99ce3d7d73e6f00a49380095e2e44056 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,16 @@ { - "dependencies": { + "devDependencies": { "file-type": "3.8.*", "is-svg": "2.0.*", "read-chunk": "2.0.*", - "svg2png-many": "*" + "svg2png-many": "*", + "webpack": "^3.0.0" + }, + "scripts": { + "dev": "webpack --progress --colors --watch" + }, + "dependencies": { + "babel-preset-es2015": "^6.24.1", + "glob": "^7.1.2" } } - diff --git a/popup.js b/popup.js new file mode 100644 index 0000000000000000000000000000000000000000..0dbd180347b04568085685c8b0503230695fe870 --- /dev/null +++ b/popup.js @@ -0,0 +1,168 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +/** global: chrome */ +/** global: browser */ + +(function () { + var popup = { + init: function () { + popup.update(['p', {}, ' '], document, {}); + + var func = function (tabs) { + (chrome || browser).runtime.sendMessage({ id: 'get_apps', tab: tabs[0], source: 'popup.js' }, function (response) { + popup.update(popup.appsToDomTemplate(response)); + }); + }; + + try { + // Chrome, Firefox + browser.tabs.query({ active: true, currentWindow: true }).then(func); + } catch (e) { + // Edge + browser.tabs.query({ active: true, currentWindow: true }, func); + } + }, + + update: function (dom) { + if (/complete|interactive|loaded/.test(document.readyState)) { + popup.replaceDom(dom); + } else { + document.addEventListener('DOMContentLoaded', function () { + popup.replaceDom(dom); + }); + } + }, + + replaceDom: function (domTemplate) { + var body = document.body; + + while (body.firstChild) { + body.removeChild(body.firstChild); + } + + body.appendChild(jsonToDOM(domTemplate, document, {})); + }, + + appsToDomTemplate: function (response) { + var appName, + confidence, + version, + categories = [], + template = []; + + if (response.tabCache && response.tabCache.count > 0) { + for (appName in response.tabCache.appsDetected) { + confidence = response.tabCache.appsDetected[appName].confidenceTotal; + version = response.tabCache.appsDetected[appName].version; + categories = []; + + response.apps[appName].cats.forEach(function (cat) { + categories.push(['a', { + target: '_blank', + href: 'https://wappalyzer.com/categories/' + popup.slugify(response.categories[cat].name) + }, ['span', { + class: 'category' + }, ['span', { + class: 'name' + }, browser.i18n.getMessage('categoryName' + cat)]]]); + }); + + template.push(['div', { + class: 'detected-app' + }, ['a', { + target: '_blank', + href: 'https://wappalyzer.com/applications/' + popup.slugify(appName) + }, ['img', { + src: 'images/icons/' + (response.apps[appName].icon || 'default.svg') + }], ['span', { + class: 'label' + }, ['span', { + class: 'name' + }, appName], (version ? ' ' + version : '') + (confidence < 100 ? ' (' + confidence + '% sure)' : '')]], categories]); + } + } else { + template = ['div', { + class: 'empty' + }, browser.i18n.getMessage('noAppsDetected')]; + } + + return template; + }, + + slugify: function (string) { + return string.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/--+/g, '-').replace(/(?:^-|-$)/, ''); + } + }; + + popup.init(); +})(); + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/run b/run index 237565872f7c918aaab7ac6689fd34f233f218b3..6401121d804bf4bb46174324c798b79d38d87168 100755 --- a/run +++ b/run @@ -8,12 +8,4 @@ if [[ -z "$(which docker)" ]]; then exit 1 fi -docker="docker run --rm -v "$(pwd):/opt/wappalyzer" -it wappalyzer/dev" - -if [[ ! -d "node_modules" ]]; then - $docker ./bin/run links - - $docker npm i -fi - -$docker ./bin/run $@ +docker run --rm -v "$(pwd):/opt/wappalyzer" -it wappalyzer/dev ./bin/run $@ diff --git a/src/apps.json b/src/apps.json index 735835afd5ab9bde90dbc19b92a2f1bf6ecadfa1..dcb5896607c4a8063ec22e51f3d7a86afb87b5fb 100755 --- a/src/apps.json +++ b/src/apps.json @@ -254,13 +254,13 @@ "Adnegah": { "cats": [ "36" - ], + ], "headers": { "X-Advertising-By": "adnegah.net" }, "html": "", + "icon": "Google Tag Manager.png", + "website": "http://www.google.com/tagmanager" + }, + "Google Wallet": { + "cats": [ + "41" + ], + "icon": "Google Wallet.png", + "script": [ + "checkout\\.google\\.com", + "wallet\\.google\\.com" + ], + "website": "http://wallet.google.com" + }, + "Google Web Server": { + "cats": [ + "22" + ], + "headers": { + "Server": "gws" + }, + "icon": "Google Web Server.png", + "website": "http://en.wikipedia.org/wiki/Google_Web_Server" + }, + "Google Web Toolkit": { + "cats": [ + "18" + ], + "env": "^__gwt_", + "icon": "Google Web Toolkit.png", + "implies": "Java", + "meta": { + "gwt:property": "" + }, + "website": "http://developers.google.com/web-toolkit" + }, + "Graffiti CMS": { + "cats": [ + "1" + ], + "headers": { + "Set-Cookie": "graffitibot[^;]=" + }, + "icon": "Graffiti CMS.png", + "implies": "Microsoft ASP.NET", + "meta": { + "generator": "Graffiti CMS ([^\"]+)\\;version:\\1" + }, + "script": "/graffiti\\.js", + "website": "http://graffiticms.codeplex.com" + }, + "Grandstream": { + "cats": [ + "22", + "39" + ], + "headers": { + "Server": "Grandstream\\/?([\\d\\.]+)?\\;version:\\1" + }, + "icon": "Grandstream.png", + "website": "http://www.grandstream.com" + }, + "Grav": { + "cats": [ + "1" + ], + "icon": "Grav.png", + "implies": "PHP", + "meta": { + "generator": "GravCMS(?:\\s([\\d.]+))?\\;version:\\1" + }, + "website": "http://getgrav.org" + }, + "Gravatar": { + "cats": [ + "19" + ], + "env": "^Gravatar$", + "html": "<[^>]+gravatar\\.com/avatar/", + "icon": "Gravatar.png", + "website": "http://gravatar.com" + }, + "Gravity Forms": { + "cats": [ + "19" + ], + "html": [ + "
]*gform_wrapper", + "
]*gform_body", + "
    ]*class=(?:\"|')[^>]*gform_fields", + "]*href=(?:\"|')[^>]*wp-content/plugins/gravityforms/css/" + ], + "icon": "gravityforms.svg", + "implies": "WordPress", + "website": "http://gravityforms.com" + }, + "Gravity Insights": { + "cats": [ + "10" + ], + "env": "^GravityInsights$", + "icon": "Gravity Insights.png", + "website": "http://insights.gravity.com" + }, + "Green Valley CMS": { + "cats": [ + "1" + ], + "html": "]+/dsresource\\?objectid=", + "icon": "Green Valley CMS.png", + "implies": "Apache Tomcat", + "meta": { + "DC.identifier": "/content\\.jsp\\?objectid=" + }, + "website": "http://www.greenvalley.nl/Public/Producten/Content_Management/CMS" + }, + "HERE": { + "cats": [ + "35" + ], + "icon": "HERE.png", + "script": "https?://js\\.cit\\.api\\.here\\.com/se/([\\d.]+)\\/\\;version:\\1", + "website": "http://developer.here.com" + }, + "HHVM": { + "cats": [ + "22" + ], + "headers": { + "X-Powered-By": "HHVM/?([\\d.]+)?\\;version:\\1" + }, + "icon": "HHVM.png", + "implies": "PHP\\;confidence:50", + "website": "http://hhvm.com" + }, + "HP": { + "cats": [ + "40" + ], + "icon": "HP.svg", + "website": "http://hp.com" + }, + "HP ChaiServer": { + "cats": [ + "22" + ], + "headers": { + "Server": "HP-Chai(?:Server|SOE)(?:/([\\d.]+))?\\;version:\\1" + }, + "icon": "HP.svg", + "implies": "HP", + "website": "http://hp.com" + }, + "HP Compact Server": { + "cats": [ + "22" + ], + "headers": { + "Server": "HP_Compact_Server(?:/([\\d.]+))?\\;version:\\1" + }, + "icon": "HP.svg", + "website": "http://hp.com" + }, + "HP ProCurve": { + "cats": [ + "37" + ], + "icon": "HP.svg", + "website": "http://hp.com/networking" + }, + "HP System Management": { + "cats": [ + "46" + ], + "headers": { + "Server": "HP System Management" + }, + "icon": "HP.svg", + "website": "http://hp.com" + }, + "HP iLO": { + "cats": [ + "22", + "46" + ], + "headers": { + "Server": "HP-iLO-Server(?:/([\\d.]+))?\\;version:\\1" + }, + "icon": "HP.svg", + "website": "http://hp.com" + }, + "HTTP Kit": { + "cats": [ + "22" + ], + "headers": { + "Server": "^http-kit" + }, + "implies": "Java", + "website": "http://http-kit.org" + }, + "HTTP-Server": { + "cats": [ + "22" + ], + "headers": { + "Server": "(?:^|[^-])\bHTTP-Server(?: ?/?V?([\\d.]+))?\\;version:\\1" + }, + "website": "http://example.com" + }, + "HTTP/2": { + "cats": [ + "19" + ], + "excludes": "SPDY", + "headers": { + "X-Firefox-Spdy": "h2" + }, + "icon": "http2.png", + "website": "http://http2.github.io" + }, + "Haddock": { + "cats": [ + "4" + ], + "html": "

    Produced by Haddock version ([0-9.]+)

    \\;version:\\1", + "script": "haddock-util\\.js", + "website": "http://www.haskell.org/haddock/" + }, + "Hammer.js": { + "cats": [ + "12" + ], + "env": "^Hammer$", + "icon": "Hammer.js.png", + "script": "hammer(?:\\.min)?\\.js", + "website": "http://hammerjs.github.io" + }, + "Handlebars": { + "cats": [ + "12" + ], + "env": "^Handlebars$", + "html": "<[^>]*type=[^>]text\\/x-handlebars-template", + "icon": "Handlebars.png", + "script": "handlebars(?:\\.runtime)?(?:-v([\\d.]+?))?(?:\\.min)?\\.js\\;version:\\1", + "website": "http://handlebarsjs.com" + }, + "Happy ICS Server": { + "cats": [ + "22" + ], + "headers": { + "Server": "Happy ICS Server(?:/([\\d.]+))?\\;version:\\1" + }, + "implies": "OmniTouch 8660 My Teamwork", + "website": "http://example.com" + }, + "Haravan": { + "cats": [ + "6" + ], + "env": "^Haravan$", + "icon": "Haravan.png", + "script": "haravan.*\\.js", + "website": "https://www.haravan.com" + }, + "Haskell": { + "cats": [ + "27" + ], + "icon": "Haskell.png", + "website": "http://wiki.haskell.org/Haskell" + }, + "HeadJS": { + "cats": [ + "12" + ], + "env": "^head$\\;confidence:50", + "html": "<[^>]*data-headjs-load", + "icon": "HeadJS.png", + "script": "head\\.(?:core|load)(?:\\.min)?\\.js", + "website": "http://headjs.com" + }, + "Heap": { + "cats": [ + "10" + ], + "env": "^heap$", + "icon": "Heap.png", + "script": "heap-\\d+.js", + "website": "http://heapanalytics.com" + }, + "Hello Bar": { + "cats": [ + "5" + ], + "env": "^HelloBar$", + "icon": "Hello Bar.png", + "script": "hellobar\\.js", + "website": "http://hellobar.com" + }, + "Hiawatha": { + "cats": [ + "22" + ], + "headers": { + "Server": "Hiawatha v([\\d.]+)\\;version:\\1" + }, + "icon": "Hiawatha.png", + "website": "http://hiawatha-webserver.org" + }, + "Highcharts": { + "cats": [ + "25" + ], + "env": "^Highcharts$", + "html": "]*>Created with Highcharts ([\\d.]*)\\;version:\\1", + "icon": "Highcharts.png", + "script": "highcharts.*\\.js", + "website": "http://highcharts.com" + }, + "Highlight.js": { + "cats": [ + "19" + ], + "icon": "Highlight.js.png", + "script": "/highlight\\.js/[\\d.]+?/highlight\\.min\\.js", + "website": "https://highlightjs.org/" + }, + "Highstock": { + "cats": [ + "25" + ], + "html": "]*>Created with Highstock ([\\d.]*)\\;version:\\1", + "icon": "Highcharts.png", + "script": "highstock(?:\\-|\\.)?([\\d\\.]*\\d).*\\.js\\;version:\\1", + "website": "http://highcharts.com/products/highstock" + }, + "Hippo": { + "cats": [ + "1" + ], + "html": "<[^>]+/binaries/(?:[^/]+/)*content/gallery/", + "icon": "Hippo.png", + "website": "http://onehippo.org" + }, + "Hogan.js": { + "cats": [ + "12" + ], + "env": "^Hogan$", + "icon": "Hogan.js.png", + "script": [ + "hogan-(?:-|\\.)([\\d.]*\\d)[^/]*\\.js\\;version:\\1", + "([\\d.]+)/hogan(?:\\.min)?\\.js\\;version:\\1" + ], + "website": "http://twitter.github.com/hogan.js" + }, + "Homeland": { + "cats": [ + "1", + "2" + ], + "headers": { + "Set-Cookie:": "_homeland_" + }, + "icon": "Homeland.png", + "implies": "Ruby on Rails", + "website": "https://gethomeland.com" + }, + "Hotaru CMS": { + "cats": [ + "1" + ], + "headers": { + "Set-Cookie": "hotaru_mobile=" + }, + "icon": "Hotaru CMS.png", + "implies": "PHP", + "meta": { + "generator": "Hotaru CMS" + }, + "website": "http://hotarucms.org" + }, + "Hotjar": { + "cats": [ + "10" + ], + "env": [ + "^HotLeadfactory$", + "^HotleadController$" + ], + "icon": "Hotjar.png", + "script": "^//static\\.hotjar\\.com/c/hotjar-", + "website": "https://www.hotjar.com" + }, + "HubSpot": { + "cats": [ + "32" + ], + "env": "^(?:_hsq|hubspot)$", + "html": "", + "icon": "InProces.png", + "script": "brein/inproces/website/websitefuncties\\.js", + "website": "http://www.brein.nl/oplossing/product/website" + }, + "Incapsula": { + "cats": [ + "31" + ], + "headers": { + "X-CDN": "Incapsula" + }, + "icon": "Incapsula.png", + "website": "http://www.incapsula.com" + }, + "Indexhibit": { + "cats": [ + "1" + ], + "html": "<(?:link|a href) [^>]+ndxz-studio", + "implies": [ + "PHP", + "Apache", + "Exhibit" + ], + "meta": { + "generator": "Indexhibit" + }, + "website": "http://www.indexhibit.org" + }, + "Indico": { + "cats": [ + "1" + ], + "headers": { + "Set-cookie": "MAKACSESSION" + }, + "html": "Powered by\\s+(?:CERN )?(?:CDS )?Indico( [\\d\\.]+)?\\;version:\\1", + "icon": "Indico.png", + "website": "http://indico-software.org" + }, + "Indy": { + "cats": [ + "22" + ], + "headers": { + "Server": "Indy(?:/([\\d.]+))?\\;version:\\1" + }, + "website": "http://indyproject.org" + }, + "InfernoJS": { + "cats": [ + "12" + ], + "env": "^Inferno$", + "icon": "InfernoJS.png", + "website": "https://infernojs.org/" + }, + "Infusionsoft": { + "cats": [ + "32" + ], + "html": [ + "]*name=\"infusionsoft_version\" [^>]*value=\"([^>]*)\" [^>]*\\/>\\;version:\\1", + "]*value=\"([^>]*)\" [^>]*name=\"infusionsoft_version\" [^>]*\\/>\\;version:\\1" + ], + "icon": "infusionsoft.svg", + "website": "http://infusionsoft.com" + }, + "InstantCMS": { + "cats": [ + "1" + ], + "headers": { + "Set-Cookie": "InstantCMS\\[logdate\\]=" + }, + "icon": "InstantCMS.png", + "implies": "PHP", + "meta": { + "generator": "InstantCMS" + }, + "website": "http://www.instantcms.ru" + }, + "Intel Active Management Technology": { + "cats": [ + "22", + "46" + ], + "headers": { + "Server": "Intel\\(R\\) Active Management Technology(?: ([\\d.]+))?\\;version:\\1" + }, + "icon": "Intel Active Management Technology.png", + "website": "http://intel.com" + }, + "IntenseDebate": { + "cats": [ + "15" + ], + "icon": "IntenseDebate.png", + "script": "intensedebate\\.com", + "website": "http://intensedebate.com" + }, + "Intercom": { + "cats": [ + "10" + ], + "env": "^Intercom$", + "icon": "Intercom.png", + "script": "(?:api\\.intercom\\.io/api|static\\.intercomcdn\\.com/intercom\\.v1)", + "website": "http://intercom.io" + }, + "Intershop": { + "cats": [ + "6" + ], + "icon": "Intershop.png", + "script": "(?:is-bin|INTERSHOP)", + "website": "http://intershop.com" + }, + "Invenio": { + "cats": [ + "50" + ], + "headers": { + "Set-cookie": "INVENIOSESSION" + }, + "html": "(?:Powered by|System)\\s+(?:CERN )?(?:CDS )?Invenio\\s*v?([\\d\\.]+)?\\;version:\\1", + "icon": "Invenio.png", + "website": "http://invenio-software.org" + }, + "Ionicons": { + "cats": [ + "17" + ], + "html": "]* href=[^>]+ionicons(?:\\.min)?\\.css", + "icon": "Ionicons.png", + "website": "http://ionicons.com" + }, + "JAlbum": { + "cats": [ + "7" + ], + "icon": "JAlbum.png", + "implies": "Java", + "meta": { + "generator": "JAlbum( [\\d.]+)?\\;version:\\1" + }, + "website": "http://jalbum.net/en" + }, + "JBoss Application Server": { + "cats": [ + "22" + ], + "headers": { + "X-Powered-By": "JBoss(?:-([\\d.]+))?\\;version:\\1" + }, + "icon": "JBoss Application Server.png", + "website": "http://jboss.org/jbossas.html" + }, + "JBoss Web": { + "cats": [ + "22" + ], + "excludes": "Apache Tomcat", + "headers": { + "X-Powered-By": "JBossWeb(?:-([\\d.]+))?\\;version:\\1" + }, + "icon": "JBoss Web.png", + "implies": "JBoss Application Server", + "website": "http://jboss.org/jbossweb" + }, + "JC-HTTPD": { + "cats": [ + "22" + ], + "excludes": "Apache", + "headers": { + "Server": "JC-HTTPD(?:/([\\d.]+))?\\;version:\\1" + }, + "icon": "JC-HTTPD.png", + "implies": "Canon", + "website": "http://canon.com" + }, + "JS Charts": { + "cats": [ + "25" + ], + "env": "^JSChart$", + "icon": "JS Charts.png", + "script": "jscharts.*\\.js", + "website": "http://www.jscharts.com" + }, + "JTL Shop": { + "cats": [ + "6" + ], + "headers": { + "Set-Cookie": "JTLSHOP=" + }, + "html": "(?:]+name=\"JTLSHOP|]+id=\"wrapper_r\"|<[^>]+(?:feed|components)/com_|]+class=\"pill)\\;confidence:50", + "icon": "Joomla.png", + "implies": "PHP", + "meta": { + "generator": "Joomla!(?: ([\\d.]+))?\\;version:\\1" + }, + "url": "option=com_", + "website": "http://joomla.org" + }, + "K2": { + "cats": [ + "19" + ], + "env": "^K2RatingURL$", + "html": "", + "icon": "Lightspeed.svg", + "script": "http://assets.webshopapp.com", + "url": "seoshop.webshopapp.com", + "website": "http://www.lightspeedhq.com/products/ecommerce/" + }, + "Lighty": { + "cats": [ + "18" + ], + "headers": { + "Set-Cookie": "lighty_version" + }, + "icon": "Lighty.png", + "implies": "PHP", + "website": "http://gitlab.com/lighty/framework" + }, + "LimeSurvey": { + "cats": [ + "19" + ], + "headers": { + "generator": "LimeSurvey" + }, + "icon": "LimeSurvey.png", + "website": "http://limesurvey.org/" + }, + "LinkSmart": { + "cats": [ + "36" + ], + "env": "^(?:_mb_site_guid$|LS_JSON|LinkSmart(?:_|$))", + "icon": "LinkSmart.png", + "script": "^https?://cdn\\.linksmart\\.com/linksmart_([\\d.]+?)(?:\\.min)?\\.js\\;version:\\1", + "website": "http://linksmart.com" + }, + "Linkedin": { + "cats": [ + "5" + ], + "icon": "Linkedin.svg", + "script": "//platform\\.linkedin\\.com/in\\.js", + "website": "http://linkedin.com" + }, + "List.js": { + "cats": [ + "12" + ], + "env": "^List$", + "icon": "List.js.png", + "script": "^list\\.(?:min\\.)?js$", + "website": "http://www.listjs.com" + }, + "LiteSpeed": { + "cats": [ + "22" + ], + "headers": { + "Server": "^LiteSpeed$" + }, + "icon": "LiteSpeed.png", + "website": "http://litespeedtech.com" + }, + "Lithium": { + "cats": [ + "1" + ], + "env": [ + "^LITHIUM$" + ], + "headers": { + "Set-Cookie": "LithiumVisitor=" + }, + "html": " ]+Powered by Lithium", + "icon": "Lithium.png", + "implies": "PHP", + "website": "http://www.lithium.com" + }, + "LiveAgent": { + "cats": [ + "52" + ], + "env": "^LiveAgent$", + "icon": "LiveAgent.png", + "website": "http://www.ladesk.com" + }, + "LiveChat": { + "cats": [ + "52" + ], + "icon": "LiveChat.png", + "script": "cdn\\.livechatinc\\.com/.*tracking\\.js", + "website": "http://livechatinc.com" + }, + "LiveJournal": { + "cats": [ + "11" + ], + "icon": "LiveJournal.png", + "url": "\\.livejournal\\.com", + "website": "http://www.livejournal.com" + }, + "LivePerson": { + "cats": [ + "52" + ], + "icon": "LivePerson.png", + "script": "^https?://lptag\\.liveperson\\.net/tag/tag\\.js", + "website": "https://www.liveperson.com/" + }, + "LiveStreet CMS": { + "cats": [ + "1" + ], + "env": "^LIVESTREET", + "headers": { + "X-Powered-By": "LiveStreet CMS" + }, + "icon": "LiveStreet CMS.png", + "implies": "PHP", + "website": "http://livestreetcms.com" + }, + "Livefyre": { + "cats": [ + "15" + ], + "env": [ + "^fyre$", + "^FyreLoader$" + ], + "html": "<[^>]+(?:id|class)=\"livefyre", + "icon": "Livefyre.png", + "script": "livefyre_init\\.js", + "website": "http://livefyre.com" + }, + "Liveinternet": { + "cats": [ + "10" + ], + "html": [ + "]*>[^]{0,128}?src\\s*=\\s*['\"]//counter\\.yadro\\.ru/hit(?:;\\S+)?\\?(?:t\\d+\\.\\d+;)?r", + "", + "", + "]*/sites/[a-z\\d]{24}/theme/stylesheets", + "icon": "Locomotive.png", + "implies": [ + "Ruby on Rails", + "MongoDB" + ], + "website": "http://www.locomotivecms.com" + }, + "Logitech Media Server": { + "cats": [ + "22", + "38" + ], + "headers": { + "Server": "Logitech Media Server(?: \\(([\\d\\.]+))?\\;version:\\1" + }, + "icon": "Logitech Media Server.png", + "website": "http://www.mysqueezebox.com" + }, + "Lotus Domino": { + "cats": [ + "22" + ], + "headers": { + "Server": "Lotus-Domino" + }, + "icon": "Lotus Domino.png", + "implies": "Java", + "website": "http://www-01.ibm.com/software/lotus/products/domino" + }, + "Lua": { + "cats": [ + "27" + ], + "headers": { + "X-Powered-By": "\bLua(?: ([\\d.]+))?\\;version:\\1" + }, + "icon": "Lua.png", + "website": "http://www.lua.org" + }, + "Lucene": { + "cats": [ + "34" + ], + "icon": "Lucene.png", + "implies": "Java", + "website": "http://lucene.apache.org/core/" + }, + "Luigi’s Box": { + "cats": [ + "10", + "29" + ], + "env": "^Luigis$", + "icon": "Luigisbox.svg", + "website": "https://www.luigisbox.com" + }, + "M.R. Inc BoxyOS": { + "cats": [ + "28" + ], + "icon": "M.R. Inc.png", + "website": "http://mrincworld.com" + }, + "M.R. Inc SiteFrame": { + "cats": [ + "18" + ], + "headers": { + "Powered-By": "M\\.R\\. Inc SiteFrame" + }, + "icon": "M.R. Inc.png", + "website": "http://mrincworld.com" + }, + "M.R. Inc Webserver": { + "cats": [ + "22" + ], + "headers": { + "Server": "M\\.R\\. Inc Webserver" + }, + "icon": "M.R. Inc.png", + "implies": [ + "M.R. Inc BoxyOS" + ], + "website": "http://mrincworld.com" + }, + "MHonArc": { + "cats": [ + "50" + ], + "html": "\\;version:\\1", + "icon": "mhonarc.png", + "website": "http://www.mhonarc.at" + }, + "MOBOTIX": { + "cats": [ + "39" + ], + "icon": "MOBOTIX.png", + "meta": { + "author": "MOBOTIX AG\\;confidence:40", + "copyright": "MOBOTIX AG\\;confidence:40", + "publisher": "MOBOTIX AG\\;confidence:40" + }, + "url": "control/userimage\\.html\\;confidence:70", + "website": "http://mobotix.com" + }, + "MODX": { + "cats": [ + "1" + ], + "env": "^MODX_MEDIA_PATH$", + "headers": { + "Set-Cookie": "SN5[a-f\\d]{12}", + "X-Powered-By": "^MODX" + }, + "html": [ + "]+>Powered by MODX", + "<(?:link|script)[^>]+assets/snippets/\\;confidence:20", + "]+id=\"ajaxSearch_form\\;confidence:20", + "]+id=\"ajaxSearch_input\\;confidence:20" + ], + "icon": "MODX.png", + "implies": "PHP", + "meta": { + "generator": "MODX[^\\d.]*([\\d.]+)?\\;version:\\1" + }, + "website": "http://modx.com" + }, + "MadAdsMedia": { + "cats": [ + "36" + ], + "env": "^setM(?:Iframe|RefURL)$", + "icon": "MadAdsMedia.png", + "script": "^https?://(?:ads-by|pixel)\\.madadsmedia.com/", + "website": "http://madadsmedia.com" + }, + "Magento": { + "cats": [ + "6" + ], + "env": [ + "^(?:Mage|VarienForm)$" + ], + "headers": { + "Set-Cookie": "frontend=\\;confidence:50" + }, + "html": [ + "