├── extension ├── resources │ ├── ico │ │ ├── 16.png │ │ ├── 48.png │ │ ├── 128.png │ │ ├── 16-dark.png │ │ ├── blockade.png │ │ ├── blockade-ori.png │ │ ├── blockade-white.png │ │ └── load.svg │ ├── fonts │ │ └── glyphicons-halflings-regular.woff2 │ ├── js │ │ ├── core │ │ │ ├── const.js │ │ │ ├── init.js │ │ │ ├── utils.js │ │ │ ├── alarms.js │ │ │ └── background.js │ │ ├── local │ │ │ ├── demo.js │ │ │ ├── warning.js │ │ │ ├── setup.js │ │ │ └── options.js │ │ └── libs │ │ │ └── blockade.js │ ├── static │ │ ├── setup.html │ │ ├── warning.html │ │ ├── demo.html │ │ └── options.html │ ├── css │ │ ├── demo.css │ │ ├── warning.css │ │ ├── setup.css │ │ └── options.css │ └── external │ │ ├── spin.min.js │ │ ├── md5.js │ │ └── lzma-worker.min.js ├── manifest.json └── _locales │ ├── he │ └── messages.json │ ├── pt_BR │ └── messages.json │ ├── hi │ └── messages.json │ ├── tr │ └── messages.json │ ├── no │ └── messages.json │ ├── ru │ └── messages.json │ ├── es │ └── messages.json │ ├── fr │ └── messages.json │ ├── de │ └── messages.json │ ├── jp │ └── messages.json │ └── en │ └── messages.json ├── screenshots ├── aws-architecture.png ├── blockade-architecture.png └── blockade-warning-page.png ├── .gitignore ├── README.rst ├── checksum.txt └── LICENSE.txt /extension/resources/ico/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/ico/16.png -------------------------------------------------------------------------------- /extension/resources/ico/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/ico/48.png -------------------------------------------------------------------------------- /extension/resources/ico/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/ico/128.png -------------------------------------------------------------------------------- /screenshots/aws-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/screenshots/aws-architecture.png -------------------------------------------------------------------------------- /extension/resources/ico/16-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/ico/16-dark.png -------------------------------------------------------------------------------- /extension/resources/ico/blockade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/ico/blockade.png -------------------------------------------------------------------------------- /screenshots/blockade-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/screenshots/blockade-architecture.png -------------------------------------------------------------------------------- /screenshots/blockade-warning-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/screenshots/blockade-warning-page.png -------------------------------------------------------------------------------- /extension/resources/ico/blockade-ori.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/ico/blockade-ori.png -------------------------------------------------------------------------------- /extension/resources/ico/blockade-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/ico/blockade-white.png -------------------------------------------------------------------------------- /extension/resources/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockadeio/chrome_extension/HEAD/extension/resources/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | ._* 4 | Thumbs.db 5 | *.sublime-project 6 | *.sublime-workspace 7 | .jshintrc 8 | bkp 9 | ssl-cert 10 | docs/s3_website.yml 11 | extension/Archive.zip -------------------------------------------------------------------------------- /extension/resources/js/core/const.js: -------------------------------------------------------------------------------- 1 | const ICON_DARK = "resources/ico/16-dark.png"; 2 | const ICON_LARGE = "resources/ico/128.png"; 3 | const ICON_LIGHT = "resources/ico/16.png"; 4 | // const OPTIONS_PAGE = "chrome://extensions/?options=" + chrome.i18n.getMessage("@@extension_id"); 5 | const OPTIONS_PAGE = "resources/static/options.html"; 6 | const SETUP_PAGE = "resources/static/setup.html"; 7 | const WARNING_PAGE = "resources/static/warning.html"; 8 | const DEFAULT_NODE = "https://api.blockade.io/"; -------------------------------------------------------------------------------- /extension/resources/js/local/demo.js: -------------------------------------------------------------------------------- 1 | function populate_demo() { 2 | document.getElementById("demoTitle").innerHTML = chrome.i18n.getMessage("demoTitle"); 3 | document.getElementById("demoP1").innerHTML = chrome.i18n.getMessage("demoP1"); 4 | document.getElementById("demoP2").innerHTML = chrome.i18n.getMessage("demoP2"); 5 | var test = "http://test.blockade.io/no-face.jpg?"; 6 | var rand = Math.floor(Math.random()*90000) + 10000; 7 | // We do this to bust up the cache 8 | document.getElementById("test").src = test + rand; 9 | var cleanup = setInterval(function () { 10 | if (localStorage["test.blockade.io"] !== null) { 11 | localStorage.removeItem("test.blockade.io"); 12 | clearInterval(cleanup); 13 | } 14 | }, 1000); 15 | } 16 | 17 | document.addEventListener('DOMContentLoaded', populate_demo); -------------------------------------------------------------------------------- /extension/resources/static/setup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blockade Setup 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

16 |

17 |

18 | Status: 19 | 20 |

21 |

22 |

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /extension/resources/static/warning.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blockade Warning 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

15 |

16 |
17 |
18 |
19 |
20 | 21 |


22 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /extension/resources/static/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blockade Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |

20 | 21 |

22 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "__MSG_appName__", 4 | "description": "__MSG_appDesc__", 5 | "default_locale": "en", 6 | "version": "1.2.8", 7 | "icons": { 8 | "16": "resources/ico/16.png", 9 | "48": "resources/ico/48.png", 10 | "128": "resources/ico/128.png" 11 | }, 12 | "browser_action": { 13 | "default_title": "__MSG_appName__", 14 | "default_icon": "resources/ico/16.png" 15 | }, 16 | "options_page": "resources/static/options.html", 17 | "permissions": ["tabs", "http://*/", "https://*/", "webRequest", 18 | "webRequestBlocking", "alarms", "notifications", 19 | "unlimitedStorage", "contextMenus"], 20 | "background": { 21 | "scripts": ["resources/external/jquery.js", "resources/external/md5.js", 22 | "resources/external/lzma-worker.min.js", 23 | "resources/js/core/utils.js", "resources/js/core/const.js", 24 | "resources/js/core/init.js", 25 | "resources/js/libs/blockade.js", "resources/js/core/alarms.js", 26 | "resources/js/core/background.js"], 27 | "persistent": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /extension/resources/css/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f7931e; 3 | } 4 | .main { 5 | height: 400px; 6 | width: 500px; 7 | position: fixed; 8 | top: 25%; 9 | left: 45%; 10 | margin-top: -100px; 11 | margin-left: -200px; 12 | color: white; 13 | } 14 | 15 | .small-link{ 16 | color: white; 17 | text-decoration : none; 18 | text-align: center; 19 | } 20 | 21 | div.a#proceed { 22 | text-align: center; 23 | } 24 | h1 { 25 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 26 | font-size: 30px; 27 | font-style: normal; 28 | font-variant: normal; 29 | font-weight: 500; 30 | line-height: 26.4px; 31 | padding-bottom: 20px; 32 | } 33 | p { 34 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 35 | font-size: 16px; 36 | font-style: normal; 37 | font-variant: normal; 38 | font-weight: 200; 39 | line-height: 25px; 40 | padding-bottom: 15px; 41 | } 42 | button { 43 | width: 100%; 44 | } 45 | .go-back { 46 | text-transform: uppercase; 47 | font-style: normal; 48 | font-variant: normal; 49 | font-weight: 500; 50 | } 51 | #proceed { 52 | color: white; 53 | text-decoration : none; 54 | text-align: center; 55 | font-size: 12px; 56 | } 57 | .glyphicon { 58 | font-size: 75px; 59 | } 60 | .logo { 61 | position: absolute; 62 | right: 0px; 63 | bottom: 10px; 64 | } -------------------------------------------------------------------------------- /extension/resources/css/warning.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #ce3426; 3 | } 4 | 5 | .main { 6 | height: 400px; 7 | width: 500px; 8 | position: fixed; 9 | top: 25%; 10 | left: 45%; 11 | margin-top: -100px; 12 | margin-left: -200px; 13 | color: white; 14 | } 15 | 16 | .small-link{ 17 | color: white; 18 | text-decoration : none; 19 | text-align: center; 20 | } 21 | 22 | div.a#proceed { 23 | text-align: center; 24 | } 25 | h1 { 26 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 27 | font-size: 30px; 28 | font-style: normal; 29 | font-variant: normal; 30 | font-weight: 500; 31 | line-height: 26.4px; 32 | padding-bottom: 20px; 33 | } 34 | p { 35 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 36 | font-size: 16px; 37 | font-style: normal; 38 | font-variant: normal; 39 | font-weight: 200; 40 | line-height: 25px; 41 | padding-bottom: 15px; 42 | } 43 | button { 44 | width: 100%; 45 | } 46 | .go-back { 47 | text-transform: uppercase; 48 | font-style: normal; 49 | font-variant: normal; 50 | font-weight: 500; 51 | } 52 | #proceed { 53 | color: white; 54 | text-decoration : none; 55 | text-align: center; 56 | font-size: 12px; 57 | } 58 | .glyphicon { 59 | font-size: 75px; 60 | } 61 | .logo { 62 | position: absolute; 63 | right: 0px; 64 | bottom: 10px; 65 | } -------------------------------------------------------------------------------- /extension/resources/css/setup.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #59983B; 3 | } 4 | 5 | .main { 6 | height: 400px; 7 | width: 500px; 8 | position: fixed; 9 | top: 25%; 10 | left: 45%; 11 | margin-top: -100px; 12 | margin-left: -200px; 13 | color: white; 14 | } 15 | 16 | .small-link{ 17 | color: white; 18 | text-decoration : none; 19 | text-align: center; 20 | } 21 | 22 | div.a#proceed { 23 | text-align: center; 24 | } 25 | h1 { 26 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 27 | font-size: 30px; 28 | font-style: normal; 29 | font-variant: normal; 30 | font-weight: 500; 31 | line-height: 26.4px; 32 | padding-bottom: 20px; 33 | } 34 | h2 { 35 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 36 | font-size: 20px; 37 | font-style: normal; 38 | font-variant: normal; 39 | font-weight: 500; 40 | line-height: 26.4px; 41 | padding-bottom: 20px; 42 | } 43 | p { 44 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 45 | font-size: 16px; 46 | font-style: normal; 47 | font-variant: normal; 48 | font-weight: 200; 49 | line-height: 25px; 50 | padding-bottom: 15px; 51 | } 52 | 53 | a:link { 54 | color: white; 55 | } 56 | a:visited { 57 | color: white; 58 | } 59 | a:hover { 60 | color: white; 61 | } 62 | a:active { 63 | color: white; 64 | } 65 | 66 | a.test { 67 | color: #fab668; 68 | } 69 | -------------------------------------------------------------------------------- /extension/resources/js/core/init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initialize our global space with a couple setup content and shared resources. 3 | */ 4 | // var _gaq = _gaq || []; 5 | // _gaq.push(['_setAccount', 'UA-89853823-2']); 6 | // _gaq.push(['_setSessionCookieTimeout', 0]); 7 | // _gaq.push(['_trackPageview', '/background']); 8 | (function() { 9 | if (typeof localStorage.cfg_init === "undefined") { 10 | localStorage.cfg_events = JSON.stringify([]); 11 | localStorage.cfg_indicators = JSON.stringify({}); 12 | localStorage.cfg_debug = false; 13 | localStorage.cfg_notifications = true; 14 | localStorage.cfg_feedback = true; 15 | localStorage.cfg_isRunning = true; 16 | localStorage.cfg_configured = false; 17 | localStorage.cfg_lastIndicatorCount = 0; 18 | localStorage.cfg_firstSync = true; 19 | localStorage.cfg_init = true; 20 | localStorage.cfg_dbUpdateTime = 5; 21 | localStorage.cfg_localDatabase = false; 22 | localStorage.cfg_channels = JSON.stringify([{ 23 | id: 0, 24 | url: 'https://api.blockade.io/', 25 | contact: 'info@blockade.io', 26 | username: '', 27 | api_key: '' 28 | }]); 29 | chrome.tabs.create({'url': SETUP_PAGE}); 30 | } 31 | // var ga = document.createElement('script'); 32 | // ga.type = 'text/javascript'; 33 | // ga.async = false; 34 | // ga.src = 'https://ssl.google-analytics.com/ga.js'; 35 | // var s = document.getElementsByTagName('script')[0]; 36 | // s.parentNode.insertBefore(ga, s); 37 | })(); 38 | 39 | var parser = document.createElement('a'); 40 | var pattern = new RegExp(/\bLTBYPASS-[0-9]{5}\b/g); 41 | var bypass = /#LTBYPASS-[0-9]{5}/; -------------------------------------------------------------------------------- /extension/resources/js/local/warning.js: -------------------------------------------------------------------------------- 1 | function getParameterByName(name, url) { 2 | if (!url) { 3 | url = window.location.href; 4 | } 5 | name = name.replace(/[\[\]]/g, "\\$&"); 6 | var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 7 | results = regex.exec(url); 8 | if (!results) return null; 9 | if (!results[2]) return ''; 10 | return decodeURIComponent(results[2].replace(/\+/g, " ")); 11 | } 12 | 13 | function populate_url() { 14 | var redirect = getParameterByName('redirect'); 15 | var parser = document.createElement('a'); 16 | parser.href = redirect; 17 | var obj = JSON.parse(localStorage[parser.hostname]); 18 | document.getElementById("details").innerHTML = JSON.stringify(obj, null, '\t'); 19 | localStorage.removeItem(parser.hostname); 20 | var rand = Math.floor(Math.random()*90000) + 10000; 21 | document.getElementById("proceed").href = redirect + "#LTBYPASS-" + rand; 22 | document.getElementById("go-back").onclick = function() { 23 | localStorage.removeItem(parser.hostname); 24 | if (document.referrer !== "") { 25 | window.history.back(); 26 | } else { 27 | window.location = "about:blank"; 28 | } 29 | }; 30 | document.getElementById("go-back").innerHTML = chrome.i18n.getMessage("warningLeaveMessage"); 31 | document.getElementById("proceed").innerHTML = chrome.i18n.getMessage("warningProceedMessage"); 32 | document.getElementById("alertTitle").innerHTML = chrome.i18n.getMessage("warningAlertTitle"); 33 | document.getElementById("alertCore").innerHTML = chrome.i18n.getMessage("warningAlertMessage", [parser.hostname]); 34 | } 35 | 36 | document.addEventListener('DOMContentLoaded', populate_url); 37 | -------------------------------------------------------------------------------- /extension/resources/js/core/utils.js: -------------------------------------------------------------------------------- 1 | function buildEvent(metadata, match, hashed) { 2 | var log = { 3 | 'analysisTime': new Date(), 4 | 'userAgent': navigator.userAgent, 5 | 'indicatorMatch': match, 6 | 'metadata': {}, 7 | 'hashMatch': hashed 8 | }; 9 | Object.assign(log.metadata, metadata); 10 | return log; 11 | } 12 | 13 | function addProtocol(url) { 14 | if (!url || url === "") { 15 | return ""; 16 | } 17 | if (!/^(f|ht)tps?:\/\//i.test(url)) { 18 | url = "https://" + url; 19 | } 20 | return url; 21 | } 22 | 23 | function removeArrayItem(arr, value) { 24 | var index = arr.indexOf(value); 25 | if (index >= 0) { 26 | arr.splice(index, 1); 27 | } 28 | return arr; 29 | } 30 | 31 | function uniq(a) { 32 | var seen = {}; 33 | return a.filter(function(item) { 34 | return seen.hasOwnProperty(item) ? false : (seen[item] = true); 35 | }); 36 | } 37 | 38 | function loadContextMenus() { 39 | chrome.contextMenus.removeAll(); 40 | chrome.contextMenus.create({"title": "Blockade", "id": "parent", 41 | "contexts": ['all']}); 42 | chrome.contextMenus.create({"title": "Send to cloud", "parentId": "parent", 43 | "contexts": ['all'], "id": "cloud"}); 44 | chrome.contextMenus.create({"title": "Options", "parentId": "parent", 45 | "contexts": ['all'], "id": "options"}); 46 | var channels = JSON.parse(localStorage.cfg_channels); 47 | for (i = 0; i < channels.length; i++) { 48 | var channel = channels[i].url; 49 | var menuItem = {"title": channel, "parentId": "cloud", 50 | "contexts": ['all'], "id": channel}; 51 | chrome.contextMenus.create(menuItem); 52 | } 53 | } -------------------------------------------------------------------------------- /extension/resources/js/local/setup.js: -------------------------------------------------------------------------------- 1 | function populate_setup() { 2 | chrome.alarms.create("setupWait", 3 | {delayInMinutes: 0, periodInMinutes: 0.1}); 4 | document.getElementById("setupTitle").innerHTML = chrome.i18n.getMessage("setupTitle"); 5 | document.getElementById("setupCore").innerHTML = chrome.i18n.getMessage("setupCore"); 6 | document.getElementById("setupQ1").innerHTML = chrome.i18n.getMessage("setupQ1"); 7 | document.getElementById("setupA1").innerHTML = chrome.i18n.getMessage("setupA1"); 8 | document.getElementById("status").innerHTML = chrome.i18n.getMessage("setupSyncing"); 9 | chrome.alarms.create("databaseUpdate", 10 | {delayInMinutes: 0.1, periodInMinutes: 5}); 11 | var statusCheck = setInterval(function () { 12 | var msg; 13 | if (parseInt(localStorage.cfg_lastIndicatorCount) > 0) { 14 | msg = "All done! Test extension."; 15 | document.getElementById('loading').style.visibility = "hidden"; 16 | document.getElementById("status").innerHTML = msg; 17 | localStorage.cfg_configured = true; 18 | clearInterval(statusCheck); 19 | var frequency = parseInt(localStorage.cfg_dbUpdateTime); 20 | chrome.alarms.create("databaseUpdate", {periodInMinutes: frequency}); 21 | chrome.alarms.clear("setupWait"); 22 | } 23 | 24 | if (parseInt(localStorage.cfg_lastIndicatorCount) === -1) { 25 | msg = "Hmm, we're having issues connecting to the cloud nodes. Please check your Internet connection and the nodes configured in the options page."; 26 | document.getElementById('loading').style.visibility = "hidden"; 27 | document.getElementById("status").innerHTML = msg; 28 | localStorage.cfg_configured = true; 29 | } 30 | }, 1000); 31 | } 32 | document.addEventListener('DOMContentLoaded', populate_setup); -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Blockade Browser Protection 2 | ========================== 3 | Blockade brings antivirus-like capabilities to users who run the Chrome browser. Built as an extension, Blockade blocks malicious resources from being viewed or loaded inside of the browser. 4 | 5 | Install 6 | ------- 7 | Blockade is currently available to a small number of alpha users. The extension can be freely downloaded through the Chrome webstore and requires no additional work in order to operate besides the cloud node URL. 8 | 9 | https://chrome.google.com/webstore/detail/blockade/dpcbdbpeiafniipmiedlceedbffiejek 10 | 11 | 12 | Purpose 13 | ------- 14 | Blockade focuses on those who are often targeted via phishing attacks and may not have a substantial capability at their disposal. Using threat intelligence mined from multiple sources (and analysts), Blockade attempts to detect attacks that may exist in the browser by monitoring web traffic requests. The primary goal for the project is to detect and prevent as many attacks as possible for those who would otherwise go left unnoticed. 15 | 16 | Architecture 17 | ------------ 18 | .. image:: /screenshots/blockade-architecture.png 19 | :alt: Reference architecture for Blockade 20 | :width: 100% 21 | :align: center 22 | 23 | Blockade is split into two pieces, cloud infrastructure and the local Chrome Extension. Intelligence is passed from the cloud infrastructure directly into the browser's local storage. Using special APIs available to extensions, Blockade will look for any web request matching a known indicator and block it from being loaded. Malicious events from Blockade are passed to the cloud infrastructure where analysts can review the findings and surface more attacks. Read more about the infrastructure here_. 24 | 25 | .. _here: https://www.blockade.io/architecture.html 26 | 27 | Support 28 | ------- 29 | If you want more details about the extension and larger project, visit the project page_. For bugs and other problems, please file a message in this repositories issue_ area. For private questions or comments, contact Brandon at info@blockade.io or find us on social media. 30 | 31 | .. _page: https://www.blockade.io/ 32 | .. _issue: https://github.com/blockadeio/chrome_extension/issues -------------------------------------------------------------------------------- /extension/resources/ico/load.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/external/spin.min.js: -------------------------------------------------------------------------------- 1 | // http://spin.js.org/#v2.3.2 2 | !function(a,b){"object"==typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return m[e]||(k.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",k.cssRules.length),m[e]=1),e}function d(a,b){var c,d,e=a.style;if(b=b.charAt(0).toUpperCase()+b.slice(1),void 0!==e[b])return b;for(d=0;d',c)}k.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.scale*d.width,left:d.scale*d.radius,top:-d.scale*d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.scale*(d.length+d.width),k=2*d.scale*j,l=-(d.width+d.length)*d.scale*2+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k 2 | 3 | 4 | 5 | Blockade Options 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 |
38 | 39 | 40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |

51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |

60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |

71 | 72 | Resources 73 |

  Setup Page: Link

74 |

  Test Page: Link

75 |

  Project Page: Link

76 |
77 |
78 |
79 |
80 | 81 |
82 | 83 |
84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /extension/resources/js/libs/blockade.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This object handles the in-memory processing of the indicators and housekeeping 3 | * for the core aspects of the extension. Without this object, the extension 4 | * has no ability to perform look-ups. 5 | */ 6 | var BlockadeIO = function() { 7 | var self = this; 8 | self.indicatorCount = 0; 9 | self.indicators = null; 10 | self.sources = []; 11 | self.active = false; 12 | 13 | /** 14 | * Attempt to load indicators from local storage or grab remotely. 15 | * @return null 16 | */ 17 | self.init = function() { 18 | if (localStorage.getItem("cfg_indicators") !== "{}") { 19 | var data = JSON.parse(localStorage.cfg_indicators); 20 | LZMA.decompress(data, function(decoded, error) { 21 | self.indicators = JSON.parse(decoded); 22 | self.active = true; 23 | self.indicatorCount = Object.keys(self.indicators).length; 24 | }); 25 | var frequency = parseInt(localStorage.cfg_dbUpdateTime); 26 | chrome.alarms.create("databaseUpdate", {periodInMinutes: frequency}); 27 | } else { 28 | console.log("Loading remotely"); 29 | chrome.alarms.create("databaseUpdate", 30 | {delayInMinutes: 0.1, periodInMinutes: 5}); 31 | } 32 | }; 33 | 34 | /** 35 | * Remove an indicator from the in-memory database one-time. 36 | * @param {string} indicator Indicator to remove from the database 37 | * @return null 38 | */ 39 | self.whitelistItem = function(indicator) { 40 | delete self.indicators[indicator]; 41 | }; 42 | 43 | /** 44 | * Add data collected from cloud nodes to a temporary hold before processing. 45 | * @param null 46 | */ 47 | self.addSource = function(data) { 48 | if (localStorage.cfg_debug === 'true') { 49 | console.log("Added source:", data); 50 | } 51 | self.sources.push(data); 52 | }; 53 | 54 | /** 55 | * Merge all the data into a memory-lookup and attempt to save locally. 56 | * @return null 57 | */ 58 | self.finalize = function() { 59 | var indicators = {}; 60 | for (var i=0; i < self.sources.length; i++) { 61 | var data = self.sources[i]; 62 | for (var j=0; j < data.indicators.length; j++) { 63 | var item = data.indicators[j]; 64 | if (indicators.hasOwnProperty(item)) { 65 | indicators[item].push(data.source); 66 | } else { 67 | indicators[item] = [data.source]; 68 | } 69 | indicators[item] = uniq(indicators[item]); 70 | } 71 | } 72 | 73 | // Attempt to save the database locally (5MB limits) 74 | LZMA.compress(JSON.stringify(indicators), 1, function(encoded, error) { 75 | if (localStorage.cfg_debug === 'true') { console.log("Compressing"); } 76 | try { 77 | localStorage.cfg_indicators = JSON.stringify(encoded); 78 | localStorage.cfg_localDatabase = true; 79 | } catch(err) { 80 | localStorage.cfg_localDatabase = false; 81 | localStorage.cfg_indicators = JSON.stringify({}); 82 | } 83 | }); 84 | 85 | // Prime the in-memory concepts 86 | self.indicatorCount = Object.keys(indicators).length; 87 | localStorage.cfg_lastIndicatorCount = self.indicatorCount; 88 | self.indicators = indicators; 89 | self.active = true; 90 | self.sources = []; 91 | if (localStorage.cfg_firstSync) { 92 | if (self.indicatorCount > 0) { 93 | localStorage.cfg_firstSync = false; 94 | localStorage.cfg_dbUpdateTime = 15; 95 | } 96 | } 97 | 98 | var msg = chrome.i18n.getMessage("dbgSavedItems", 99 | [self.indicatorCount]); 100 | if (self.indicatorCount > 101 | parseInt(localStorage.cfg_lastIndicatorCount)) { 102 | if (localStorage.cfg_notifications === 'true') { 103 | chrome.notifications.create('info', { 104 | type: 'basic', 105 | iconUrl: ICON_LARGE, 106 | title: chrome.i18n.getMessage("notifyIndicatorSyncTitle"), 107 | message: msg 108 | }, function(notificationId) { 109 | msg = chrome.i18n.getMessage("dbgNotificationCreated"); 110 | if (localStorage.cfg_debug === 'true') { console.log(msg); } 111 | }); 112 | } 113 | } 114 | // Reduce the update frequency now that the database is fully setup 115 | var frequency = parseInt(localStorage.cfg_dbUpdateTime); 116 | chrome.alarms.create("databaseUpdate", {periodInMinutes: frequency}); 117 | }; 118 | }; 119 | 120 | var blockade = new BlockadeIO(); 121 | blockade.init(); 122 | -------------------------------------------------------------------------------- /extension/resources/js/core/alarms.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Process the events generated that are stored locally. 3 | * 4 | * Checks with local storage to see if we have any events and then sends them 5 | * along to the remote cloud node. If there are no events, we bail to avoid 6 | * making the call with no data. 7 | */ 8 | function processEvents() { 9 | var events = JSON.parse(localStorage.cfg_events); 10 | // Prune out our demo code 11 | for (var i = events.length -1; i >= 0 ; i--) { 12 | if (events[i].indicatorMatch === "test.blockade.io") { 13 | events.splice(i, 1); 14 | } 15 | } 16 | 17 | if (events.length === 0) { 18 | localStorage.cfg_events = JSON.stringify([]); 19 | return false; 20 | } 21 | 22 | var channels = JSON.parse(localStorage.cfg_channels); 23 | if (channels.length === 0) { 24 | msg = chrome.i18n.getMessage("dbgNoServer"); 25 | if (localStorage.cfg_debug === 'true') { console.log(msg); } 26 | return false; 27 | } 28 | 29 | var promises = []; 30 | for (var i=0; i < channels.length; i++) { 31 | var data = new FormData(); 32 | var matchedEvents = []; 33 | for (var j=0; j < events.length; j++) { 34 | toNotify = blockade.indicators[events[j].hashMatch]; 35 | if (toNotify.indexOf(channels[i].id) === -1) { 36 | continue; 37 | } 38 | events[j].contact = channels[i].contact; 39 | matchedEvents.push(events[j]); 40 | } 41 | var properties = {method: "POST", 42 | body: JSON.stringify({'events': matchedEvents}), 43 | headers: {"Content-Type": "application/json"}}; 44 | promises.push(fetch(channels[i].url + 'send-events', properties)); 45 | } 46 | Promise 47 | .all(promises) 48 | .then(function(response) { 49 | var blobs = []; 50 | for (var i=0; i < response.length; i++) { 51 | blobs.push(response[i].json()); 52 | } 53 | return Promise.all(blobs); 54 | }) 55 | .then(function(blobs) { 56 | localStorage.cfg_events = JSON.stringify([]); 57 | var msg = chrome.i18n.getMessage("dbgProcessedEvents"); 58 | if (localStorage.cfg_debug === 'true') { console.log(msg); } 59 | // _gaq.push(['_trackEvent', 'extension', 'submit_events']); 60 | }) 61 | .catch(function(error) { 62 | var message = chrome.i18n.getMessage("notifyRequestError", 63 | ["URL", error.message]); 64 | chrome.notifications.create('alert', { 65 | type: 'basic', 66 | iconUrl: ICON_LARGE, 67 | title: chrome.i18n.getMessage("notifyRequestErrorTitle"), 68 | message: message 69 | }, function(notificationId) { 70 | msg = chrome.i18n.getMessage("dbgNotificationCreated"); 71 | if (localStorage.cfg_debug === 'true') { console.log(msg); } 72 | }); 73 | // _gaq.push(['_trackEvent', 'extension', 'submit_events_failure']); 74 | }); 75 | } 76 | 77 | /** 78 | * Update the local signature database with our definitions from the cloud. 79 | * 80 | * The storage structure for indicators is aimed to make the processing of 81 | * web requests as efficient as possible. Having multiple buckets to store 82 | * indicators based on the starting positions ensures we are only loading a 83 | * subset of the entire indicator definition. 84 | */ 85 | function databaseUpdate() { 86 | var channels = JSON.parse(localStorage.cfg_channels); 87 | if (channels.length === 0) { 88 | msg = chrome.i18n.getMessage("dbgNoServer"); 89 | if (localStorage.cfg_debug === 'true') { console.log(msg); } 90 | return false; 91 | } 92 | localStorage.cfg_isRunning = true; 93 | 94 | var promises = []; 95 | for (var i=0; i < channels.length; i++) { 96 | promises.push(fetch(channels[i].url + 'get-indicators')); 97 | } 98 | Promise 99 | .all(promises) 100 | .then(function(response) { 101 | var blobs = []; 102 | for (var i=0; i < response.length; i++) { 103 | blobs.push(response[i].json()); 104 | } 105 | return Promise.all(blobs); 106 | }) 107 | .then(function(blobs) { 108 | for (var i=0; i < blobs.length; i++) { 109 | if (!blobs[i].success) { 110 | continue; 111 | } 112 | // Promises should return in order 113 | blobs[i].source = channels[i].id; 114 | blockade.addSource(blobs[i]); 115 | } 116 | blockade.finalize(); 117 | // _gaq.push(['_trackEvent', 'extension', 'get_indicators']); 118 | }) 119 | .catch(function(error) { 120 | var message = chrome.i18n.getMessage("notifyRequestError", 121 | ['URL', error.message]); 122 | chrome.notifications.create('alert', { 123 | type: 'basic', 124 | iconUrl: ICON_LARGE, 125 | title: chrome.i18n.getMessage("notifyRequestErrorTitle"), 126 | message: message 127 | }, function(notificationId) { 128 | msg = chrome.i18n.getMessage("dbgNotificationCreated"); 129 | if (localStorage.cfg_debug === 'true') { console.log(msg); } 130 | }); 131 | localStorage.cfg_lastIndicatorCount = -1; 132 | // _gaq.push(['_trackEvent', 'extension', 'get_indicators_failure']); 133 | }); 134 | } 135 | 136 | /** 137 | * Chrome alarm processor that will fire any time an alarm is generated. 138 | */ 139 | chrome.alarms.onAlarm.addListener(function(alarm) { 140 | if (alarm.name == "processEvents") { 141 | processEvents(); 142 | } else if (alarm.name == "databaseUpdate") { 143 | databaseUpdate(); 144 | } 145 | }); -------------------------------------------------------------------------------- /extension/resources/js/local/options.js: -------------------------------------------------------------------------------- 1 | function add_node() { 2 | var url = document.getElementById('cfg_cloudUrl').value; 3 | if (url === '') { 4 | var error = document.getElementById("error_message"); 5 | error.innerHTML = "Address can't be blank."; 6 | setTimeout(function() { 7 | error.innerHTML = ""; 8 | }, 3000); 9 | return false; 10 | } 11 | url = addProtocol(url); 12 | if (url.slice(-1) !== "/") { 13 | url += "/"; 14 | } 15 | var contact = document.getElementById('cfg_contact').value; 16 | if (contact.indexOf('@') === -1 && contact !== '') { 17 | var error = document.getElementById("error_message"); 18 | error.innerHTML = "Email was invalid."; 19 | setTimeout(function() { 20 | error.innerHTML = ""; 21 | }, 3000); 22 | return false; 23 | } 24 | var channels = JSON.parse(localStorage.cfg_channels); 25 | var result = $.grep(channels, function(e){ return e.url == url; }); 26 | if (result.length > 0) { 27 | var error = document.getElementById("error_message"); 28 | error.innerHTML = "Address is already used."; 29 | setTimeout(function() { 30 | error.innerHTML = ""; 31 | }, 3000); 32 | return false; 33 | } 34 | var username = document.getElementById('cfg_username').value; 35 | var api_key = document.getElementById('cfg_apiKey').value; 36 | channels.push({'id': channels.length, 'url': url, 'contact': contact, 37 | 'username': username, 'api_key': api_key}); 38 | localStorage.cfg_channels = JSON.stringify(channels); 39 | document.getElementById('cfg_cloudUrl').value = ''; 40 | document.getElementById('cfg_contact').value = ''; 41 | document.getElementById('cfg_username').value = ''; 42 | document.getElementById('cfg_apiKey').value = ''; 43 | chrome.alarms.create("databaseUpdate", 44 | {delayInMinutes: 0.1, periodInMinutes: 1.0}); 45 | location.reload(); 46 | loadContextMenus(); 47 | } 48 | 49 | function save_options() { 50 | $('#save').toggleClass('btn-primary').toggleClass('btn-success'); 51 | select_fields = ['cfg_debug', 'cfg_notifications', 'cfg_feedback']; 52 | for (i = 0; i < select_fields.length; i++) { 53 | var radio = document.getElementsByName(select_fields[i]); 54 | for (var j = 0, length = radio.length; j < length; j++) { 55 | if (radio[j].checked) { 56 | localStorage[select_fields[i]] = radio[j].value; 57 | break; 58 | } 59 | } 60 | } 61 | 62 | var url = document.getElementById('cfg_cloudUrl').value; 63 | if (url !== '') { 64 | add_node(); 65 | } 66 | 67 | // Kick-off alarms to perform an update on the indicators 68 | chrome.alarms.create("processEvents", 69 | {delayInMinutes: 0.1, periodInMinutes: 0.5}); 70 | chrome.alarms.create("databaseUpdate", 71 | {delayInMinutes: 0.1, periodInMinutes: 1.0}); 72 | localStorage.cfg_configured = true; 73 | 74 | // Update status to let user know options were saved. 75 | var status = document.getElementById("status"); 76 | status.innerHTML = chrome.i18n.getMessage("optionsConfigSaved"); 77 | setTimeout(function() { 78 | status.innerHTML = ""; 79 | $('#save').toggleClass('btn-primary').toggleClass('btn-success'); 80 | }, 3000); 81 | } 82 | 83 | // Restores select box state to saved value from localStorage. 84 | function restore_options() { 85 | var select_fields = ['cfg_debug', 'cfg_notifications', 'cfg_feedback']; 86 | for (i = 0; i < select_fields.length; i++) { 87 | var radio = document.getElementsByName(select_fields[i]); 88 | for (var j = 0, length = radio.length; j < length; j++) { 89 | if (radio[j].value == localStorage[select_fields[i]]) { 90 | radio[j].checked = true; 91 | } 92 | } 93 | } 94 | 95 | var channels = JSON.parse(localStorage.cfg_channels); 96 | var rows = []; 97 | for (i = 0; i < channels.length; i++) { 98 | var columns = []; 99 | columns.push($('').append(channels[i].url)); 100 | columns.push($('').append(channels[i].contact)); 101 | columns.push($('').append(channels[i].username)); 102 | columns.push($('').append(channels[i].api_key)); 103 | var btn = $('