Integrating Jscrambler with Ember

Last updated: 4 June 2021

Framework version tested: 2.16.0 ● 3.21.0

Github: Ember repository

Introduction

Ember or Ember.js is a JavaScript front-end framework that aims to improve the process of building highly scalable websites. This framework makes use of a templating library, Handlebars, and with the help of a DOM rendering engine, Glimmer, delivers a performance based solution to render and update data on your app. It's among the most common front-end frameworks along with React, Angular and Vue.js.

Ember allows developers to create plugins that add functionality to a deploy or any related operation. On this tutorial we will guide you through the process of adding and configuring the Jscrambler into the build process of an Ember application.

Getting started with Ember

We will begin by using the app provided on Ember's tutorial section - Tutorial. You can either go through the guided process of creating the app or head to their github page and download a working version of the app. Github.

Finally, follow the instructions to get the application up and running and then head to the next section where we will address the integration with Jscrambler.

Integrating Jscrambler with Ember

If you haven't tried Jscrambler out before reading this tutorial, please consider reading the Getting Started Guide which will walk you through the steps on how to protect your application. This will make this section easier to grasp. It will also teach you how to configure Jscrambler and use a custom configuration.

To make this integration easy and more straightforward we created an Ember plugin that you can add to your build process.

Simply by installing it and adding the configuration we are ready to protect the files and have them deployed to production.

Further details on the plugin can be found on its repository

Let's now include the Jscrambler plugin into our project Start off by installing the plugin by simply running:

ember install ember-cli-jscrambler

After installing the plugin it will automatically hook into the build pipeline but we need to add some options in order to protect the application files.

Jscrambler Configuration

To configure Jscrambler to properly work we should create a file on the root of the project called .jscramblerrc. This file will then be loaded by the client.

On this example we will be using the parameters for one of our default templates.

{
  "keys": {
    "accessKey": <ACCESS_KEY>,
    "secretKey": <SECRET_KEY>
  },
  "applicationId": <APPLICATION_ID>,
  "params": [
    {
      "name": "whitespaceRemoval"
    },
    {
      "name": "identifiersRenaming",
      "options": {
        "mode": "SAFEST"
      }
    },
    {
      "name": "dotToBracketNotation"
    },
    {
      "name": "deadCodeInjection"
    },
    {
      "name": "stringConcealing"
    },
    {
      "name": "functionReordering"
    },
    {
      "options": {
        "freq": 1,
        "features": [
          "opaqueFunctions"
        ]
      },
      "name": "functionOutlining"
    },
    {
      "name": "propertyKeysObfuscation"
    },
    {
      "name": "regexObfuscation"
    },
    {
      "name": "booleanToAnything"
    }
  ],
  "areSubscribersOrdered": false,
  "useRecommendedOrder": true,
  "jscramblerVersion": "stable",
  "sourceMaps": false
}

Feel free to use the sample above just by filling the missing fields (accessKey, secretKey, applicationId, and jscramblerVersion).

Special note for jscramblerVersion which at the time of writing we have versions 5.1 and 5.2 available.

The presented set of parameters offers a good base protection level for most cases but feel free to build from here by adding and/or removing transformations. Also if you need some help feel free to reach us so we can guide you through the process.

Protecting our App

Everything done with the setup and Jscrambler should now protect the files everytime the user runs the production build.

ember build --environment=production

Ember usually creates a dist folder with the generated files and on this step you can check by yourself that the files were protected.

In this example we pretend to exclude the vendor file from the protection step as we don't consider it to having any business logic that's worth concealing.

To do so edit the file ember-cli-build.js and under the EmberApp constructor add the option to exclude assets/vendor.js.

The file should look as it follows:

/* eslint-env node */
'use strict';

const EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  let app = new EmberApp(defaults, {
    'ember-cli-jscrambler': {
      exclude: ['assets/vendor.js']
    }
  });

  return app.toTree();
};

Next time you issue a production build, the vendor file will no longer be protected.

Code Annotations

Unfortunately, the current integration process does not support code annotations as during the build process every form of annotations (comment and string annotations) are removed by the bundling step before the Jscrambler plugin runs, we are currently working towards a solution to provide a better usage of this feature.

Real World Example

Because the Ember build process is similar on every app, we will take a real world example and use the Jscrambler plugin to protect the application.

We will be using travis-web. As first steps lets start by following the instruction given on the Github project.

After installing all the required dependencies we are ready to include the Jscrambler plugin.

ember install ember-cli-jscrambler

The ember-cli-build.js already has some options being passed to the ember app so we will just append the Jscrambler plugin configuration with the exclude option for the vendor file at the end of the object, similarly to what we have done on the sample project from the Ember tutorial. In the end it should look like the following:

/* eslint-env node */
'use strict';

const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const Funnel = require('broccoli-funnel');

export default function () {
  let fingerprint;

  if (process.env.DISABLE_FINGERPRINTS) {
    fingerprint = false;
  } else {
    fingerprint = {
      // FIXME this is probably not desired
      exclude: ['images/emoji', 'images/logos', 'images/pro-landing/flag*', 'images/team'],
      extensions: ['js', 'css', 'png', 'jpg', 'gif', 'map', 'svg']
    };

    if (process.env.TRAVIS_ENTERPRISE) {
      fingerprint.prepend = '/';
    } else {
      let assetsHost = process.env.ASSETS_HOST;
      if (assetsHost) {
        if (assetsHost.substr(-1) !== '/') {
          assetsHost = assetsHost + '/';
        }
        fingerprint.prepend = assetsHost;
      } else if (process.env.DEPLOY_TARGET) {
        const s3Bucket = require('./config/deploy')(process.env.DEPLOY_TARGET).s3.bucket;
        fingerprint.prepend = '//' + s3Bucket + '.s3.amazonaws.com/';
      }
    }
  }

  const app = new EmberApp({
    'ember-cli-babel': {
      includePolyfill: true,
    },
    babel: {
      blacklist: ['regenerator'],
      plugins: [
        'transform-decorators-legacy',
        'transform-class-properties',
      ]
    },
    fingerprint: fingerprint,
    sourcemaps: {
      enabled: true,
      extensions: ['js']
    },
    'ember-prism': {
      'components': ['scss', 'javascript', 'json'], // needs to be an array, or undefined.
      'plugins': ['line-highlight']
    },
    svg: {
      optimize: false,
      paths: [
        'public/images/stroke-icons',
        'public/images/svg'
      ]
    },
    sassOptions: {
      extensions: 'sass'
    },
    'ember-cli-jscrambler': {
      exclude: ['assets/vendor.js']
    }
  });

  app.import('bower_components/pusher/dist/pusher.js');
  app.import('bower_components/jquery-timeago/jquery.timeago.js');

  app.import('bower_components/js-emoji/demo/emoji.css');
  app.import('bower_components/js-emoji/lib/emoji.js');

  app.import('bower_components/waypoints/lib/jquery.waypoints.js');
  app.import('bower_components/waypoints/lib/shortcuts/inview.js');

  const emojiAssets = new Funnel('bower_components/emoji-data/img-apple-64', {
    destDir: '/images/emoji'
  });

  return app.toTree(emojiAssets);
};

The .jscramblerrc file can be the same used on the tutorial app.

After making the changes you can run the build and check that the main bundle file was protected and the vendor file was not, just like on the other application.

Known Problems

At the time of this writing, apart from the annotations issues already described above we haven't found any issue related to the transformations support. However, since there are so many use cases and different scenarios, please reach us if you find any issue related to the protection of your application.

You can also check other integrations.