These days, programmers often work in large, collaborative teams that produce dozens of different deliverables at the same time.
If you’re working on an image editor, for example, some of the components in it might also be released as standalone programming libraries that other people can use.
You might have a GUI version of your product, as well as command line tools that do the same sort of work in a different way.
And you might publish for several different platforms, including mobile devices, each of which needs its own build of each version.
For example, imagine that you’re part of an team that makes image editing software, and that you’re working on a low level function called
multiply_brightness_matrix(), trying to make it run faster.
The code you’re looking at might be no more than 100 lines of code in one file, out of 10,000,000 lines in 10,000 files in the whole project.
Such localised changes are probably fairly easy to test on their own: you should be able to confirm that your new code produces the same output as the old code, at least when used in isolation.
But now also imagine that your code is used by an image filter called
add_sparkle_highlight() that will end up in a library called
ITEFFECT.DLL that will, in turn, be built into your own company’s flagship
ImageTouchup software on Windows, Mac and Android.
How will your modest and apparently unproblematic changes affect everyone else in the ecosystem? Will all the products that use your new code still work correctly? If they work differently, will the changes be an improvement or a step backwards? Will your changes affect any other recent changes that your colleagues are working on?
As a popular proverb warns us:
For want of a nail the shoe was lost. For want of a shoe the horse was lost. For want of a horse the rider was lost. For want of a rider the message was lost. For want of a message the battle was lost. For want of a battle the kingdom was lost. And all for the want of a horseshoe nail.
To address this sort of problem, modern software development processes often rely on what’s called continuous integration (CI), or frequent integration, whereby you automatically test the impact even of small changes in your code on your entire ecosystem, instead of waiting for a daily or weekly build that tries everyone’s recent updates at the same time.
The theory is obvious and community-centric: the earlier you find a problem, the sooner you can fix it and the fewer other people you’ll affect in the troubleshooting process.
You can’t do that by hand, so a number of toolkits are available these days that can automatically rebuild all your software, and at least partially re-test it, almost any time any programmer tries out any change.
CI is an impressive luxury that engineering and manufacturing companies don’t have. For instance, it would be an absurd waste of time and raw materials for an automotive company to build a new car from scratch every time a designer changed the look of the fuel filler cap. Rebuilding software isn’t free, because it requires servers, hard disks, networking, air conditioning and the electricity to run them all, but you don’t consume real materials every time you compile a new version of your software, so that continuous integration can, more or less, be just that.
One popular and widely used toolkit for the CI process is called Jenkins:
Jenkins can not only rebuild projects after each change, but even automatically approve, sign and deploy newly-built versions into one or more test environments…
…from where, if they pass all the necessary tests, they might even flow on automatically into your distribution system – in the physical world, what would be called your “supply chain”.
Try bribing the butler
You can see where this is going.
A bug or security flaw in a toolkit like Jenkins could have a massive impact, because Jenkins is part of the infrastructure that helps you decide whether to trust your new software or not.
And Jenkins not only presents a large attack surface on its own, it can be bolstered, boosted and extended by dozens of popular plugins, not part of the core software, that can bring their own security risks to the party.
The most recent Jenkins security update, for example (2017-04-10), addressed at least 32 arbitrary remote code execution bugs, both in the software itself and in many of its plugins.
We’ve warned about the risk of server-side plugins many times before, for example when the popular WordPress plugin TimThumb opened up holes even on servers where WordPress itself was up-to-date.
Application plugins are easy to forget about: once you’ve dealt with the operating system itself and your apps, it often feels as though your patching work is done.
An an aside, we’ve also warned for years against browser plugins, notably Flash and Java, because they have been fertile bug-hunting grounds for cybercrooks who couldn’t get past the browser: if you can’t bribe the Laird, try bribing the butler instead.
What do do?
Bugs of this sort may sound harmless on the surface – after all, rogue programmers with the right to submit files to your build system could just write rogue instructions right into the code they upload.
But you can think of these holes as “metacoding” bugs, where a rogue programmer could submit perfectly legitimate code changes that would be passed as improvements, yet could at the same time sneakily subvert the build process itself.
That could leave you with official software, built officially from the official source code…
…but with some unofficial “secret sauce” mixed in.
🌶 MORE SECRET SAUCE: When genuine Mac software goes rogue ►
🌶 MORE SECRET SAUCE: World’s biggest Linux distro infected with malware ►
🌶 MORE SECRET SAUCE: 36 Android devices ship with malware ►
🌶 MORE SECRET SAUCE: XCodeGhost turns your Mac into an iPhone virus generator ►