Package Scan Web Tool

Written by Pete Corey on Sep 28, 2015.

This past week I’ve decided to put a little more love into my east5th:package-scan project. In an attempt to lower the barrier of entry for using the tool, I’ve given it a super-simple web interface. Check it out at scan.east5th.co!

The tool lets you select or drop in a Meteor versions file, which will then be compared against the list of packages with known security issues. If any matches are found, it’ll display those vulnerable package alerts on the page.

I made a conscious decision to not send versions files to the server to do the scanning. Instead, I pull the alerts.json file into the browser, along with a browserfied version of semver, and run the scan directly in on the client. This way, the users’ versions files never leave their browser.

Be sure to try it out, and more importantly, contribute if you know of any vulnerable package versions that we’re not reporting!

Exporting ES6 Classes From Meteor Packages

Written by Pete Corey on Sep 23, 2015.

To celebrate the release of Meteor 1.2 and built-in support for ES6 syntax, I’ve been playing with implementing some of my favorite Object Oriented design patterns in JavaScript. While doing this, I quickly ran into an interesting quirk of the Meteor package system. I began by writing creating a class in a Meteor package:

class CommandHandler {
  ...
}

Next, I added an export for CommandHandler in the package’s package.js file:

api.export('CommandHandler');

But interestingly, CommandHandler was undefined in my Meteor application. What’s going on here?


A quick session in the Babel REPL shows that a plain class decleration will compile down to a locally scoped function:

class CommandHandler {}
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var CommandHandler = function CommandHandler() {
  _classCallCheck(this, CommandHandler);
};

This type of function declaration will not be picked up by the api.export method. Only package-level variables, or variables declared without a var, let, or const, are exportable. A quick fix to our class definition would be to tie our class definition to the global scope:

CommandHandler = class CommandHandler {}

This declaration compiles down to the following JavaScript:

CommandHandler = function CommandHandler() {
  _classCallCheck(this, CommandHandler);
};

We can get more concise about it and use an unnamed class expression:

CommandHandler = class { }

Which compiles down to:

CommandHandler = (function () {
  function _class() {
    _classCallCheck(this, _class);
  }

  return _class;
})();

When using unnamed class expressions, it’s important to remember that certain features like this.constructor.name cannot be used within your class.

Notice that the CommandHandler function is now being declared on the global scope. Now api.export will happily pick up our CommandHandler declaration and we can use it within our Meteor application.

Happy ES6ing!

Never Forget Where Your Code Runs

Written by Pete Corey on Sep 21, 2015.

I often warn about the dangers of blindly trusting user provided data. There are times, though, when it’s easy to forget where your data comes from. Sir Charles Watson brought up a good example of this when he found a security issue revolving around an insecure onCreateUser hook.

Imagine that somewhere in your application you’ve defined an onCreateUser callback like the one below:

Accounts.onCreateUser(function(options, user) {
  user.isAdmin = !!options.isAdmin;
  return user;
});

The isAdmin flag is set in a method used by administrators to create new admin users:

Meteor.methods({
  createAdminUser: function(email) {
    check(username, String);

    var user = Meteor.users.findOne(this.userId);
    if (user && user.isAdmin) {

      var newUserId = Accounts.createUser({
        email: email,
        isAdmin: true
      });

      Accounts.sendEnrollmentEmail(newUserId);
    }
  }
});

This looks secure enough. We’re asserting that the username is a String, and that the currently logged in user has the appropriate authorization to create a new admin user. No one could tamper with this method and fraudulently create an admin account.

Unfortunately, we’re forgetting that createUser can also be called from the client. Additionally, any client can provide their own options argument. An in-the-know user could create an admin account by running this code in their browser console:

Accounts.createUser({
  username: "loladmin",
  password: "loladmin",
  isAdmin: true
});

After running the above line, the user would be logged in and granted full administrator permissions.


There are two obvious fixes to this security issue. The first, and by far the most straight-forward is to simply disable user account creation on the client using forbidClientAccountCreation. This may not be the best solution, though, as it would prevent users from signing up with your application. Only administrators would be able to create users through server-side methods.

A better solution may be to remove the onCreateUser callback and move the admin creation functionality into an entirely separate method:

Meteor.methods({
  createAdminUser: function(email) {
    check(email, String);

    var user = Meteor.users.findOne(this.userId);
    if (user && user.isAdmin) {

      var newUserId = Accounts.createUser({
        email: email
      });

      Meteor.call(“makeUserAdmin”, newUserId);
      Accounts.sendEnrollmentEmail(newUserId);
    }
  },
  makeUserAdmin: function(userId) {
    check(userId, String);
    var user = Meteor.users.findOne(this.userId);
    if (user && user.isAdmin) {
      Meteor.users.update(userId, {
        $set: {
          isAdmin: true
        }
      });
    }
  }
});

Using this solution, users can still register for your application using createUser on the client, and admins can still create new admin users using the createAdminUser method.


This issue is indicative of a broader issue with developing Meteor. Developers often forget that most of the code they’re writing is isomorphic. Most of the code you write can (and will be) run on both the server and the client.

Sometimes it’s easy to lose track of where your code can be executed. Is the file you’re editing buried deep within a server folder? Is the code you’re writing wrapped in a Meteor.isServer guard? Is the code you’re writing in any way triggerable by a client-side action? These questions can be cognitive burdens, but they’re important to keep in mind at all times when developing secure Meteor applications.