├── package.js ├── lib └── package-scan.js ├── README.md └── data └── alerts.json /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'east5th:package-scan', 3 | version: '0.0.8', 4 | summary: 'Looks through your installed package list looking for dangerous packages.', 5 | git: 'https://github.com/East5th/package-scan', 6 | documentation: 'README.md', 7 | debugOnly: true 8 | }); 9 | 10 | Package.onUse(function(api) { 11 | api.versionsFrom('1.2.0.1'); 12 | api.use('http'); 13 | api.addFiles('lib/package-scan.js', 'server'); 14 | api.addAssets('data/alerts.json', 'server'); 15 | }); 16 | 17 | Npm.depends({ 18 | semver: "4.3.3" 19 | }); 20 | -------------------------------------------------------------------------------- /lib/package-scan.js: -------------------------------------------------------------------------------- 1 | var fs = Npm.require('fs'); 2 | var readline = Npm.require('readline'); 3 | var semver = Npm.require('semver'); 4 | 5 | var url = 'https://raw.githubusercontent.com/east5th/package-scan/master/data/alerts.json'; 6 | 7 | var alerts = []; 8 | var packages = []; 9 | 10 | function processLine(line) { 11 | line = line.trim(); 12 | if (line.length) { 13 | var split = line.split('@'); 14 | packages.push({ 15 | name: split[0], 16 | version: split[1] 17 | }); 18 | } 19 | } 20 | 21 | function skipPackage(package) { 22 | var settings = Meteor.settings["package-scan"] || {}; 23 | var ignore = settings.ignore || []; 24 | return ignore.indexOf(package) > -1; 25 | } 26 | 27 | function scanPackages() { 28 | packages.forEach(function(package) { 29 | if (skipPackage(package.name)) { 30 | return; 31 | } 32 | (alerts[package.name] || []).forEach(function(alert) { 33 | if (semver.satisfies(package.version, alert.range)) { 34 | console.warn(package.name + '@' + package.version + ' (' + alert.range + '): ' + alert.alert); 35 | if (alert.links && alert.links.length) { 36 | alert.links.forEach(function(link) { 37 | console.warn(' '+link.name+': '+link.url); 38 | }); 39 | } 40 | } 41 | }); 42 | }); 43 | } 44 | 45 | Meteor.startup(function() { 46 | HTTP.get(url, function(err, res) { 47 | if (err) { 48 | console.error('package-scan: ', err); 49 | return {}; 50 | } 51 | 52 | try { 53 | alerts = JSON.parse(res.content); 54 | } catch (e) { 55 | console.error('package-scan: Unable to parse alerts.json!'); 56 | return {}; 57 | } 58 | 59 | if (process.env.PWD) { 60 | var path = process.env.PWD + '/.meteor/versions'; 61 | } else { 62 | var path = process.cwd().split('.meteor')[0] + '.meteor/versions'; 63 | } 64 | 65 | readline.createInterface({ 66 | input: fs.createReadStream(path), 67 | output: process.stdout, 68 | terminal: false 69 | }) 70 | .on('line', processLine) 71 | .on('close', scanPackages); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Package Scan 2 | 3 | This Meteor package is intended to be used as a security precaution to warn against the use of packages with known security issues. 4 | 5 | Want to try Package Scan without adding it to your project? Try the [drag-and-drop web interface](http://scan.east5th.co/). 6 | 7 | ## Installation 8 | 9 | ``` 10 | meteor add east5th:package-scan 11 | ``` 12 | 13 | ## Usage 14 | 15 | On startup, Package Scan will parse your versions file and compare the packages being used in your project with a list of packages with known security issues. If a matching package is found, an alert describing the issue will be displayed in the server's logs. This is a debug only package, which means it will never be built into your production application. 16 | 17 | For example, if any version of [insecure](https://github.com/meteor/meteor/tree/devel/packages/insecure) is being used by your project, you'll see the following alert on startup: 18 | 19 | ``` 20 | insecure (*): This package is not appropriate for use in production applications! 21 | ``` 22 | 23 | ##### Ignoring Alerts 24 | 25 | Alerts for specific packages can be ignored by setting up a `package-scan` block in your `settings.json` file: 26 | 27 | ``` 28 | { 29 | "package-scan": { 30 | "ignore": ["insecure"] 31 | } 32 | } 33 | ``` 34 | 35 | This will prevent any alerts about the `insecure` package from being displayed in your console. 36 | 37 | ## Why Use Package Scan? 38 | 39 | Meteor's isomorphic package system is amazingly powerful. Being able to add seamless front to back-end functionality with a single package leads to huge productivity boosts, but it can also lead to potential issues. Do you know exactly what is going on in that package you just added? Are you using old, outdated versions of packages that may have issues? Is that package even being maintained? Did you know that the [client is given a list of packages being used by your project](http://www.1pxsolidtomato.com/2015/04/24/black-box-meteor-package-scanning/)? 40 | 41 | The goal of Package Scan is to act as a safeguard against the use of packages with known security vulnerabilities. When a questionable package or version of a package is detected, Package Scan will alert you. 42 | 43 | Package Scan will only work with the community's support and involvement. Please help add and remove alerts as they're discovered and resolved. 44 | 45 | ## Contributing 46 | 47 | Package Scan is still a very young project and can be improved immensely! Feel free to send pull requests! 48 | 49 | ##### Adding Alerts 50 | 51 | Alerts can be added by submitting a pull request against the ```data/alerts.json``` file. ```alerts.json``` holds an array of semver ranges and alerts associated with that range for each vulnerable package. 52 | 53 | This project uses [node-semver](https://github.com/npm/node-semver) to parse and compare package versions. The ```range``` associated with each ```alert``` is a [semver range](https://github.com/npm/node-semver#ranges). Take a look at the semver documentation to better understand how to use the range syntax. 54 | -------------------------------------------------------------------------------- /data/alerts.json: -------------------------------------------------------------------------------- 1 | { 2 | "aldeed:tabular": [ 3 | { 4 | "range": ">=0.2.2", 5 | "alert": "A malicious selector can be passed into tabular_getInfo.", 6 | "links": [ 7 | { 8 | "name": "Github Issue", 9 | "url": "https://github.com/aldeed/meteor-tabular/issues/187" 10 | } 11 | ] 12 | } 13 | ], 14 | "allow-deny": [ 15 | { 16 | "range": "<1.0.9", 17 | "alert": "By sending a specially crafted WebSocket payload, a malicious client can execute an update operation on individual MongoDB documents in violation of the collection’s allow and deny rules, when particular third-party packages are installed.", 18 | "links": [ 19 | { 20 | "name": "Meteor allow/deny vulnerability disclosure", 21 | "url": "https://forums.meteor.com/t/meteor-allow-deny-vulnerability-disclosure/39500" 22 | } 23 | ] 24 | } 25 | ], 26 | "aslagle:reactive-table": [ 27 | { 28 | "range": "<0.6.8", 29 | "alert": "Publications are not re-evaluated when the current user changes.", 30 | "links": [ 31 | { 32 | "name": "Github Issue", 33 | "url": "https://github.com/aslagle/reactive-table/issues/164" 34 | } 35 | ] 36 | } 37 | ], 38 | "autopublish": [ 39 | { 40 | "range": "*", 41 | "alert": "This package is not appropriate for use in production applications!" 42 | } 43 | ], 44 | "babrahams:editable-json": [ 45 | { 46 | "range": "<=0.5.1", 47 | "alert": "Allows updates to any Mongo collection and is not meant for production use" 48 | } 49 | ], 50 | "cfs:standard-packages": [ 51 | { 52 | "range": "*", 53 | "alert": "Manipulating an uploaded image that has been renamed to a different filetype (e.g. a Quicktime movie renamed as a JPG file) and/or trying to manipulate bad/corrupt images can cause your Meteor server to repeatedly crash.", 54 | "links": [ 55 | { 56 | "name": "Github Issue 1", 57 | "url": "https://github.com/CollectionFS/Meteor-CollectionFS/issues/550" 58 | }, 59 | { 60 | "name": "Github Issue 2 with Work-Around", 61 | "url": "https://github.com/CollectionFS/Meteor-CollectionFS/issues/227" 62 | } 63 | ] 64 | } 65 | ], 66 | "insecure": [ 67 | { 68 | "range": "*", 69 | "alert": "This package is not appropriate for use in production applications!" 70 | } 71 | ], 72 | "jabbslad:basic-auth": [ 73 | { 74 | "range": "*", 75 | "alert": "DDP connections are not protected by Basic Auth", 76 | "links": [ 77 | { 78 | "name": "Summary", 79 | "url": "http://blog.east5th.co/2016/03/28/bypassing-basic-auth-through-ddp-connections/" 80 | } 81 | ] 82 | } 83 | ], 84 | "kit:basic-auth": [ 85 | { 86 | "range": "*", 87 | "alert": "This package is theoretically vulnerable to a timing attack.", 88 | "links": [ 89 | { 90 | "name": "Github Issue", 91 | "url": "https://github.com/cwaring/meteor-basic-auth/issues/2" 92 | } 93 | ] 94 | }, 95 | { 96 | "range": "*", 97 | "alert": "DDP connections are not protected by Basic Auth", 98 | "links": [ 99 | { 100 | "name": "Summary", 101 | "url": "http://blog.east5th.co/2016/03/28/bypassing-basic-auth-through-ddp-connections/" 102 | } 103 | ] 104 | } 105 | ], 106 | "meteorhacks:kadira": [ 107 | { 108 | "range": "<=2.30.3", 109 | "alert": "An attack payload can be delivered remotely, resulting in a Denial-of-Service and require the application to be restarted.", 110 | "links": [ 111 | { 112 | "name": "Denial-of-Service disclosure for Meteor APM/Kadira agent", 113 | "url": "https://forums.meteor.com/t/denial-of-service-disclosure-for-meteor-apm-kadira-agent/41127" 114 | }, 115 | { 116 | "name": "Github Pull Request", 117 | "url": "https://github.com/meteorhacks/kadira/pull/264" 118 | } 119 | ] 120 | } 121 | ], 122 | "meteorhacks:sikka": [ 123 | { 124 | "range": "*", 125 | "alert": "", 126 | "links": [ 127 | { 128 | "name": "Github Issue", 129 | "url": "https://github.com/meteorhacks/sikka/issues/6" 130 | }, 131 | { 132 | "name": "Crater comment describing issue", 133 | "url": "https://crater.io/posts/D8Puif9p2qqcy8KfF/introducing-sikka-a-firewall-for-your-meteor-app#3BGEpf5gH8sLzLzYP" 134 | } 135 | ] 136 | } 137 | ], 138 | "orionjs:accounts": [ 139 | { 140 | "range": "1.1.0 - 1.4.2", 141 | "alert": "Any user can change the password of any other user.", 142 | "links": [ 143 | { 144 | "name": "Github Issue", 145 | "url": "https://github.com/orionjs/orion/issues/263" 146 | } 147 | ] 148 | } 149 | ], 150 | "orionjs:config": [ 151 | { 152 | "range": "<=1.1.0", 153 | "alert": "Secret Keys can be queried from terminal client side. https://github.com/orionjs/orion/issues/210", 154 | "links": [ 155 | { 156 | "name": "Github Issue", 157 | "url": "https://github.com/orionjs/orion/issues/210" 158 | } 159 | ] 160 | } 161 | ], 162 | "rubaxa:sortable": [ 163 | { 164 | "range": "<=1.2.0", 165 | "alert": "Arbitrary numeric fields in any collection can be incremented by an attacker.", 166 | "links": [ 167 | { 168 | "name": "Github Commit", 169 | "url": "https://github.com/RubaXa/Sortable/commit/aa80521cb25b7df076826ca5f8b3d321a35944dc" 170 | } 171 | ] 172 | } 173 | ], 174 | "telescope:core": [ 175 | { 176 | "range": "0.2.0 - 0.21.1", 177 | "alert": "This version of Telescope is vulnerable to a privilege escalation attack.", 178 | "links": [ 179 | { 180 | "name": "Changelog", 181 | "url": "https://github.com/TelescopeJS/Telescope/blob/master/History.md#v0211-slugscope" 182 | } 183 | ] 184 | } 185 | ] 186 | } 187 | --------------------------------------------------------------------------------