Time to Introduce Dependency Hygiene

This post is inspired by the recent supply chain attack on coa, a popular command-line parser package for NodeJS,. and the discussions that followed that – both on LinkedIn and in the local InfoSec Slack community. This attack was not the first one, and definitely won’t be the last one. It’s true that Node modules are small and bring a lot of sub-dependencies – but they are usually super-focused on what they are doing. The speed of development in Node is largely based on this modularity, which comes with a lot of inherent risks. So, how do you mitigate those risks?

It’s about time to introduce Dependency Hygiene.

TL;DR: Use lock files. Pin your dependencies. Run audits. Patch frequently – but don’t rush fresh upgrades in.

Now, let’s dive deeper into those items. I’ll explain what do I mean by each one of them and how they are relevant in the mitigation of the modern supply chain attacks.

My primary working environment nowadays is NodeJS-based, so the examples will come from this world, but most of what I’m going to tell applies to other ecosystems as well.

Continue reading “Time to Introduce Dependency Hygiene”

Do not follow HTTP redirects with Gaxios

Earlier today I was enhancing some relatively old piece of NodeJS code, so I decided to convert it from axios to gaxios along the way. Most of the work was pretty transparent, since much smaller and better maintained gaxios is pretty much a drop-in replacement for axios, which seems to be stuck in it’s v0.x days forever.

However, there was one request that stood apart.

This request actually resulted in HTTP redirect, and my goal was to intercept the target URL and extract some query string parameters. In axios, that was straight-forward – it was enough to allow at most 0 redirects, and also make sure that HTTP 302 is not treated as an error:

const axios = require('axios');

const response = await axios.request({
  ...
  maxRedirects: 0,
  validateStatus: (status) => (status === 302)
});
const url = response.headers.location;

Yet, the same configuration produced different outcome in gaxios – the request was failing with "max redirects reached" error. My attempt to adjust maxRedirects value to 1 along with keeping status validation in place was not successful either – gaxios just issued another request to the new target.

To my surprise, quick googling did not reveal any relevant results. I took that as a hint that this is not only doable, but likely so trivial that nobody had to ask – so I continued with the digging. One of the merged PRs caused me to realize that gaxios is basically a wrapper on top of the Fetch API, and the configuration object is passed down to fetch implementation with minimal modifications. From here, the solution was simple –

const { request } = require('gaxios');

const response = await request({
  ...
  redirect: 'manual',
  validateStatus: (status) => (status === 302)
});
const url = response.headers.location;

Enjoy!

Optimization of Lodash styling and bundle size

Lodash is one of the most widely used libraries in modern web development. Whether your webapp is based on Angular, React, Vue or something more exotic – chances that you use Lodash are pretty high. Lodash provides tons of utility methods, making your code more fast and elegant.

However, those tons of useful methods come with a cost – Lodash “weights” more than 70 KB of minified code. This is a significant addition even for the heavy apps. Moreover, chances are you are using just a handful of methods from the whole library. Tree shaking provided by your favorite packing tool could help, but here it comes the next issue – there are several possible ways you can import Lodash in your ES6-based code, potentially making it “unshakable”:

// Method 1:
// import the whole library
import _ from 'lodash';

// Method 2:
// import only relevant methods
import { map, find, filter } from 'lodash';

// Method 3:
// import individual modules
import map from 'lodash/map';
import find from 'lodash/find';
import filter from 'lodash/filter';

Each one of those methods has own pros and cons (and they are described in depth in this excellent article from BlazeMeter), but in many cases personal choice of the developer plays more significant role than formal recommendations. In big projects dealing with the styling of Lodash imports alone may quickly become a mess, and a single “whole library” import will eliminate all your bundle size optimization efforts.

Fortunately, there is a way to handle both size and styling issues together – with the help of Lodash plugins for Eslint and Babel.

Eslint plugin for Lodash enforces consistent and recommended styling for Lodash usage. I’ve found rules coming from plugin:lodash/canonical configuration to be a perfect fit for our React application. This set enforces whole package imports with _ as the name for Lodash variable, enforces usage of Lodash methods over native counterparts when available, and much more. The only caveat I found was in the testing code, where it confused enzyme.find() with native method. Fortunately, we have separate eslint configuration for testing code, so it was pretty easy to tweak the offending rule without sacrificing the style of the application code.

Babel plugin for Lodash implicitly transforms recommended _ imports to per-module imports, reducing “tree-shaked” bundle size. For the app that I was experimenting with, applying both plugins effectively reduced the size of the app bundle by more than 42 KB – but your mileage may vary.

In addition, I spent some time looking at webpack plugin for Lodash, but ultimately decided to skip it. The plugin strips out significant parts of internal Lodash implementation, claiming that those parts are rarely used. While the plugin is configurable, maintaining this configuration will be a pain. Moreover, the code that is unit-tested may be significantly different from the code that is being packaged, especially around edge cases. In my opinion, potential gain in bundle size does not justify the risks of the implementation.

There is one more thing that is worth mentioning in this context. Don’t use Lodash chaining – neither explicit (with _.chain(arr)...) nor implicit (with _(arr)...), since this again will import the whole Lodash library. There are more details about it in this Medium post. In some cases, where chaining seems to be the most simple and elegant way of doing things, see if you can get away with flow and lodash/fp combination:

import { flow, map, compact } from 'lodash/fp';

const result = 
  flow(
    map(...),
    compact
  )(arr);

You can read more about functional programming with Lodash here.

Happy Lodashing!

Summary of Cache-Related HTTP Headers

Long ago (long before the first post in this blog!) I’ve composed a list of cache-related HTTP headers, so I would not need to go through the trial-and-error process of guessing the right combination more than once. Recently I got another question about caching and it took me a lot of time to recall where I saw this list last time. So now I’m placing it here.

Please treat the explanations below as quick and incomplete summary. For full specification of “Pragma”, “Cache-Control” and “Expires” headers refer to HTTP/1.1 specification.

Caching in HTTP 1.1

Following directive does not prevent caching despite its name. It allows caching of the page, but specifies that the cache must ask the originating web server if the page is up-to-date before serving the cached version. So the cached page can still be served up if the originating web server says so. Applies to all caches.

Cache-Control: no-cache

Following directive tells the browser that the page has expired and must be treated as stale. Should be good news as long as the caches obey.

Expires: Thu, 01 Jan 1970 00:00:00 GMT

Following directive specifies that the page contains information intended for a single user only and must not be cached by a shared cache (e.g. a proxy server).

Cache-Control: private

Following directive specifies that a cache must not store any part of the response or the request that elicited it.

Cache-Control: no-store

Following directive tells the cache that the maximum acceptable staleness of a page is 0 seconds.

Cache-Control: max-stale=0

Caching in HTTP 1.0

Following directive is the only cache control directive for HTTP 1.0, so use it in addition to any HTTP 1.1 cache control headers you include.

Pragma: no-cache

Continue reading “Summary of Cache-Related HTTP Headers”

Static Internal IP Address for Windows Azure VMs

WinAzureOn the same day I published my Azure Newbie Notes post, which mentioned, among other things, the inability to set static IP address for VMs, things changed a bit. Then-new v0.7.3 of Windows Azure PowerShell added several cmdlets that allow handling of static internal IP addresses. This came without any official announcements, but was covered in several blog posts on TechNet. No doubt this is a must-have feature – many services just won’t operate correctly without constant IP address, with DNS server is probably the most notable among them. Unfortunately, configuring static IP address via web-based Azure Management Console is not available yet.

[ Here you can find brief explanation on how to install and configure Azure PowerShell, along with the some related links. ]

Several posts and articles with useful samples related to static IP addresses:

Here and in the samples below “MyVM” is the name of the virtual machine we want to update and “MyCloudService” is the name of the cloud service this VM belongs to.

Probably the most useful sample nowadays is one that shows how to take existing VM and set it to have a static IP address. Although most of the articles say you have to use an available (that is, free) IP address, you can use the same IP address that is already assigned to the specific VM. Also, pay attention that Update-AzureVM cmdlet reboots the VM. Note that in the sample run below I’m configuring MyVM to the same IP address it already has. Of course, it makes sense to run the first part of the command alone first to make sure you are going to update the right VM.

Get-AzureVM -ServiceName MyCloudService -Name MyVM | Set-AzureStaticVNetIP -IPAddress 10.0.0.100 | Update-AzureVM

Another useful command is to check whether a VM already has a static IP address configured or not. For VMs with static IP address configured, this IP will be displayed, otherwise only the diagnostic output of the first part will be shown.

Get-AzureVM -ServiceName MyCloudService -Name MyVM | Get-AzureStaticVNetIP

Continue reading “Static Internal IP Address for Windows Azure VMs”