In the summer of 2015 we switched Microsoft Social Engagement to a monthly release cycle. This move brought a lot of challenges and forced us to automate much of our release and deployment process. In the old days of six-month release cycles we had been running several manual checks twice a year to ensure that we were fully compliant with all the recommendations in the Microsoft Security Development Lifecycle. Browser app developers should all be familiar with this set of coding rules such as don't use eval, always use strict mode, and don't change document.domain, plus many others that may not be so obvious. So in 2015 our team sat down, compiled a list of all the SDL checks we need to perform, and found a way to check every single one as part of our continuous integration build.
Our browser app is written in TypeScript and we've been running TSLint on every build since the start of the project to check and validate our source code. TSLint is a static analysis tool that runs in Node.js, and it makes assertions about your source code in order to keep it clean, find possible bugs, uncover security issues, and enforce a consistent style. If you write some suspicious code, then TSLint will find it and flag it as an error (you typically want to treat this as a build failure). For example, if you create a new Function object and pass a string parameter then TSLint will report an error:
|MyFile.ts[15, 30]: forbidden: Function constructor with string arguments|
There are currently 59 rules in TSLint. They range from checking style (whitespace and curly-brace placement rules) to finding common bugs (enforcing triple-equals comparisons and assignments in conditionals) to uncovering security issues (forcing 'use strict' and avoiding eval). There is a lot there, and at Microsoft Social Engagement we've had great success by using these rules as we've quadrupled in size since the start of the project!
But these 59 rules do not cover all of the SDL (or even most of it). So at last year's //oneweek Hackathon we created a set of extra TSLint rules that you can use on your project. Our project is called tslint-microsoft-contrib and currently contains 47 rules of our own. By using the two projects together we were able to fully automate all of our SDL checks. And both TSLint and tslint-microsoft-contrib are open source, have a business-friendly OSS license, and are published on npmjs.org, so you can start using them on your project today.
- Analyzing TypeScript source will result in fewer false positives introduced during compilation
- Rules benefit from having type information available
- Line numbers can be accurately reported and mapped to a source file
- Modern IDEs like VS Code and Visual Studio support in-editor warnings when using the TSLint Plugin or Web Analyzer
Automating the SDL Checks with TSLint
You'll need to enable 15 rules in order to fully automate the SDL checks. We documented these rules on the microsoft-tslint wiki page titled "TSLint and the Microsoft Security Development Lifecycle". You'll almost certainly want to enable more rules than just these 15, but it is a good set of rules with which to get started. Let's dive in and see how we can configure and run these rules.
Using TSLint and tslint-microsoft-contrib
TSLint is a Node.js application so you can run it from the command line. However, you'll probably want to run this as part of your build instead. You can easily integrate into a Grunt or Gulp build, but if you're intent on running it from the command line then refer to the command line instructions on the TSLint site. I'll show you how to add the tools to a Grunt build. There are a few steps we'll have to follow:
- Install TSLint and tslint-microsoft-contrib
- Add a tslint task to your build
- Configure the SDL rules
Install TSLint and tslint-microsoft-contrib
From your project root directory (where your Gruntfile.js lives), install the TSLint and tslint-microsoft-contrib packages from the command line:
|npm install grunt-tslint --save-dev
npm install tslint-microsoft-contrib --save-dev
If you did this correctly then you should see two new lines in your package.json file (one line for each of the dependencies). You should also see some new directories inside your node_modules folder.
Now open your Gruntfile.js and make sure the plugin gets loaded. Add the following code directly inside your build's exported function:
If you did this correctly then you should still be able to run your build without errors. There is not yet a tslint task, and we'll add that next.
Add a tslint task to your build
Now we need to tell the Grunt build two things: which files to scan and which rules to use. This is fairly easy, and a good starting point is to edit your Gruntfile.js file by adding the following "tslint" task definition inside your grunt.initConfig call:
There are three key pieces of information to understand about this task definition:
- The rulesDirectory property points to the additional rules that you installed in the previous step
- The rules property is your list of rules you want to enable. For this example we only enabled one rule
- The src property is the fileset that you with to scan. This example assumes that all your source files are in a sub-directory called "src" and the pattern will match both .ts and .tsx files (in case you are using React, like we are)
If you did this correctly then the grunt command "grunt tslint" should run on your build. The output (if there were no rule violations) should look something like this:
|$ grunt tslint
Running "tslint:files" (tslint) task
>> 103 files lint free.
Now you're ready to start configuring some rules!
Configure the SDL rules
There are many, many rules to enable. Start with the 15 SDL rules by defining your ruleset like this:
Now re-run your "grunt tslint" command and fix those errors!
A use strict Note
Enabling the use-strict rule will force every TypeScript source file to contain a use-strict statement. However, be careful with this! Your TypeScript compiler or your build process might bundle several .ts files into a single .js output file. The use-strict directive should appear once per .js output file. Where exactly it should appear depends on which module system you are using. Be sure to perform a full build and check that 'use strict' appears at the correct location in your .js output.
How It Works
TSLint uses the TypeScript Compiler API to read each file and compile it into an abstract syntax tree (AST). TSLint then exposes an easy to use visitor pattern API to each rule so that you can read and verify attributes of the tree. So if you want to make a new rule about your catch-block format then you'd just write a small Typescript class that overrides the visitCatchClause method and makes some checks. Or if you want to analyze return statements then you'd override the visitReturnStatement method. The hardest part of writing a new rule is to figure out which one of the 70 visitor methods you want to override.
The TSLint site has good documentation on how to write your own rule. However, both TSLint and tslint-microsoft-contrib are happy to accept new rules into their ruleset… so if you are going to write a new rule then please consider contributing it back to one of the projects.
There are many rules for you to consider enabling. Besides the 100 or so rules in TSLint and microsoft-tslint-contrib, there are also some Angular specific rules in the ng2lint project and many ES6 related rules in the tslint-eslint-rules project. This should keep you busy for some time!
So please install and run our tools on your project. If you have ideas for new rules or if you find some false positives then please report them in our issue tracker. Have fun and happy coding!