Software Supply Chains
1. Definition
A Supply Chain is the sequence of processes and flows involved in the production and distribution of a software product.
2. Current Software Development Reality
Today, if we take a closer look at any piece of software, proprietary or open source, we will find out that event if the development team that delivers that piece of software, develops a lot of its code, an important part of this code is developed by other organizations, any time from today ot 30 years ago.
This happens for different reasons, the main one is that organizations that build software, reuses and produces open source software. While open-source software is not essential for building a supply chain, most of the reused code is open source because of its maturity in terms of quality and licensing.
Thus, organizations deliver software that is partially developed internally, but that also contains software developed by other organizations. This other organizations use different development life cycles: each piece of software evolves independently from the others.
In one hand, using more mature third-party software improves reliability (safety & security), while requiring less resources (computing power, storage, bandwidth, development time). In other hand, this reduces the control on the software: mitigate security risks due to the software supply chains.
3. Development Lifecycle
3.1. In-House
In-House software development often follows the same workflow, independently of the programming environment and from the organization itself:
-
Developers use source code database, named version controls system, such as Git, to store the source code and other development artifacts: scripts, configurations, etc. Different developers modifies the source code.
-
Changes on the source code trigger (sometimes automatically) a build system, which is a set of scripts and tools that translates the source code into a binary software artifact that is ready to be deployed. Building includes compiling, linking to external libraries, code generation, unit and integration testing, static analysis, etc. In short, a sequence of steps that ensure that the new version keeps or improves the quality and does not introduce regressions.
-
The build system produces a distributable software artifact, which is then deployment on a distribution platform, such as a Web Server or an App Store distributes the software artifact to its uses.
Developer do not write everything from scratch, they reuse plenty of external tools: operating systems, language compilers, database management systems, web servers, external libraries, etc.
These tools are specified in the build system configuration scripts and stored in the version control system. There are several build systems available, for instance Maven and Gradle for Java and Kotlin; npm for Javascript ans Typescript, Cargo for , etc. These build systems will retrieve all pieces of software needed in the build.
3.2. With Software Supply Chains
The introduction of dependency manager software transformed the development lifecycle. The final software product no longer depends on some local developers and libraries, but a complex chain of suppliers, spread all over the world, using different developers, tools, delivery cycles, from active or inactive projects.
The local development lifecycle remains almost the same, the only difference is that before building deployable software artifacts, the build system retrieves other software artifacts and integrates them to the deployable artifact.
Thus, each time a build is triggered, the build system checks for updates on the imported artifacts and fetch the new artifacts. While it is not mandatory, the fetched artifacts are often (always) open source.
It is important to that the software product that we build and distribute has been built, configured, and controlled by our software development team as well as by external teams teams on which we have absolutely no control. There is nothing the developers can do about it: software systems are too complex nowadays and they cannot build everything from scratch, they must rely on external artifacts.
The build system integrates local artifacts, but it also fetches libraries from external sources of code, called package managers. When the developer think about reusing an external artifact, the build system will fetch the binary of this external library from a package manager, which is outside the scope of the code that is managed and controlled by the local (in-house) software developers. Thus, those package managers are typically databases that contain binary code for external artifacts.
For instance, your favorite NPM package or Maven artifact. It is important to understand that these binaries are themselves pieces of code, which have been built and developed by software development teams, which have put the code in the version control system, this code has been built, and then eventually pushed to the package manager.
As you would expected, this process is more complex than this, the build system not only depends on the local source code and one external package manager, but it can depend from many external package managers the developers will include in the build script.
4. Software Supply Chains Risks
During software development, local developers introduce (and fix) bugs. This is as expected as that eternal teams introduce and deploy bugs to the binaries they distribute, and these bugs will be integrated in their software product?
This is a risk that comes with the benefits of reusing code. In one hand, you do not have to build, to maintain, or to evolve, but in the other hand, this benefit comes at one risk which is you might have a bug in your in your product. Naturally, you are reliable for this bug, even if it actually comes from an external library.
This is a first risk we have when adopting software supply chains, and the question is what can developers do to mitigate this risk.
Another important risk is related to security. One thing that happens is that since the artifacts you use are are developed and maintained by open source communities, and a malicious developer, through different ways, can eventually insert a malicious piece of code that will end up in your software product.
This malicious code can have different behaviors, for instance, leak data so it will be possible to spy on anyone who is using your product, open a backdoor, insert a malware or a virus inside the library. If this code is accepted by the library developers, this malicious code will be part of the binary distribution, and eventually end in your code.
For several serious open source projects, such as the Apache libraries for Java, the Linux kernel, or the Bitcoin platform, their version control systems are under scrutiny and it is very hard for anyone to push code there.
Nonetheless, it is possible and has already happened. Malicious developers have used social engineering to insert some malicious code inside the repo.
Another thing that malicious users do to attack the software supply chain is to directly push malicious libraries, instead of trying to push malicious code inside the code of an existing library. They push brand new libraries containing malicious code inside the package manager.
The question one may ask is why would any developer use a new library, containing malicious code? One way of achieving this, is an attack called typo squatting, which consists in inserting into a trusted package manager a library whose name is almost the same as a legitimate library.
For instance, suppose that a very popular JSON serialization package named "json", and that someone pushes an alternative package named "JSON", "Json", or even "ison". Another way to achieve typosquatting is to use letters that do not have the same UTF-8 encoding, but which visually look very much alike. Surprisingly, this type of squatting has actually worked, and lots of products have eventually embedded a malicious packages, because the name was very similar to a legitimate library.
If this malware ends inside your software product, and then you distribute your product to thousands of clients, you will distribute a malicious code to thousands of people.
4.1. Supply Chain Attacks
Among all possible risk, the security ones are the most alarming.
For instance, in March 2022, versions 10.1.1 and 10.1.2 of popular npm module node-ipc contained the code deleting file system content of IPs geo-located in Belarus or Russia.[1]
Malicious code added in Git [3], but history got re-written. No external attackers, but politicized and disgruntled open-source maintainers.
In November 2018, the NPM package event-stream was attacked.
This package has more than 1.5 million downloads per week and has 1,600 dependent packages.
A malicious user (right9control) asked the original maintainer to give him ownership and succeeded.
Then, the new owner added malicious dependency flatmap-stream to the build script.
The new package containing the malicious code only was published to the NPM package the. Malware and decryption only ran in the context of a release build of the bitcoin wallet copay
This Malware was discovered only by accident, the use of deprecated command resulting in a warning
5. Robust Software Supply Chain
In one hand, the software supply chain is therefore necessary, because developers cannot rebuild everything from scratch, in any domain: entertainment, energy, healthcare, transportation. This obviously the case of web based applications, which have to rely on external libraries.
On the other hand, it is a risk and that today some people in companies, as well as in research labs are building technology in order to mitigate these risks.
Thus, the question is how what can we do to mitigate these risks? A lot of people look at this from the perspective of the open source, or of the software ecosystem on which you rely.
5.1. Understand the open source ecosystem
5.1.1. Licenses
Another important issue, which is not about only software technology, but still is an issue in terms of liability and business is licenses. Typically, external artifacts do not have the same license, thus we do not have the same rights when using then. At the end, when you deliver a product made from external artifacts, every line of code has a license, some lines have your other own license, while others have different licenses. What we must know here is if all these licenses are compatible with each other.
5.1.2. Truck factor
Another way to look at the development ecosystem when you want to reuse an artifact is ask different questions, for instance:
-
Who is building it?
-
How often is it fixed?
-
How big is the community of developers?
-
How easy is it to make a change?
-
How many people are in charge?
This measurement of risks is called the truck factor. It expresses the risks of a project to be abandoned. If a artifact has a small number of developers, then there is a high risk because if something happens to this developer, suddenly the package that you reuse is not maintained not updated anymore.
The truck factor is based on a number of metrics, about the way the code is developed, more than the code itself.
5.2. Secure the development pipeline
Another way to provide a robust software supply chains is to secure the development pipeline.
As mentioned earlier, the risk starts from one developer making a change in the code somewhere and then this change is passed to a version control system, and then it passes through build system to create a deliverable binary, which is pushed onto a package manager or a deployment platform.
All along the line, the question is how are we sure that the binary we get has not been tempered with, in other words, how do we ensure that the delivered binary contains no more and no less than the code that has been pushed by the developers.
5.2.1. Signed commits
There is a number of lines of research about how to sign commits, so the person who pushes a change has to sign with some cryptographic keys, and about how to have reproducible builds, to ensure that when someone builds a given piece of code, he gets a binary, and when he does it for the second time, he will get exactly the same binary, with the same hash code for both binaries
This is very complex line of research, very few people today do reproducible builds in the open source community. The most famous ones are Ubuntu and Tor.
The Bitcoin foundation, for instance, has a very strict process of build, so that they can ensure that the binary they build is the result of changes in their source code.
6. Dependency Management
6.1. Surviving Software Dependencies
Russ Cox, who is the main developer of the package manager for the Go language, identifies several challenges and proposes 6 ways to take car of software dependencies [Cox:2019]:
-
Inspect
-
Test
-
Abstraction
-
Isolate
-
Avoid
-
Upgrade
The first is to inspect the dependencies. This is related to the metrics previously mentioned. Given one external library, we analyze how often is it updated, how often is it maintained, how many people maintain it.
The second one is to test it. It is not because someone reuses a piece of code that we should assume it is correct.
Another proposal is to add an abstraction layer between your code and this external library to reduce the coupling. When you need to change this dependency, you will not need to change your code too much. Abstraction is related to the idea of isolating the dependencies.
A fifth way is to avoid external libraries. Today, it is very easy to reuse external code. This is a good thing, which has been promoted by the community. However, there is also a counterpart, developers tend to integrate in their products more libraries than he actually needs, so his advice is to avoid using libraries you do not need.
The last one is upgrade, which is a very generic guideline in terms of security. It is valid for software dependencies, as well as for any product that you have, it is always a good idea get the latest version.
6.2. Dependency Watch and Upgrade
There are today bots that automatically watch dependency and vulnerability databases, which are programs who are in charge of looking at your code and external libraries and check whether your external dependencies are up to date and then send you a warning, for instance, in the form of a pull request.
This kind of bots are available and running on the GitHub forge, for instance. In 2019, GitHub acquired Dependabot, which scans Java and JavaScript dependencies, compares with a vulnerability database, and proposes pull requests. GitHub claims to have one million Dependabot pull requests merged.
Renovate is a similar bot, which is open source, multi-language, has its own vulnerability database, and proposes automatic pull requests
6.3. Avoid Dependencies
Reusing external dependencies is not only necessary, but is also a good practice. However, developers should not depend on more dependencies he actually needs.
Depclean is an open-source tool for Java/Maven products. Depclean automatically generates a debloated build script for your projects. It detects dependencies that are declared but not necessary.
7. Ensuring Reproducible Builds
7.1. From Bill Of Materials to Software BOM (SBOM)
A BOM (for Bill of Materials) is an exhaustive list of all the components of a product, rom a single screw to electronic components to the parts that make up the packaging. It’s your shopping list for creating your product.
A software bill of materials, or SBOM, is a list of all the components and dependencies (proprietary and open source) that an application uses. SBOMs often also include information on the version of each component and dependency. This is important because security problems often only affect specific versions of the software.
But what does it actually mean?
SBOMs are generally formatted in a specific way, and can usually be represented as XML or JSON files. That said, the exact nature of an SBOM varies according to the SBOM standard - such as CycloneDX, SPDX or SWID - that teams choose to store and structure SBOM information. However, despite minor differences, all SBOM formats focus on making it easy to determine at a glance what “goes into” each application used by a company.
Why are SBOMs important?
SBOMs are the only way to systematically track an application’s components and dependencies. By extension, they provide the information needed to determine whether components or dependencies are exposed to safety risks.
SBOMs are also important because, in some cases, they may be required by regulators or customers. For example, in 2021, the US federal government announced its intention to require SBOMs from suppliers who do business with government agencies, as part of an effort to ensure that agencies have some visibility into the software on which they depend.
What’s more, even if SBOMs aren’t strictly required by your customers, partners or regulators, generating them is an excellent way of showing that you take software supply chain security seriously. SBOMs offer external stakeholders the peace of mind of having greater visibility over software components and dependencies, and the ability to determine whether third-party software exposes them to risk.
The most efficient way to generate an SBOM is to take advantage of SBOM tools, which automatically track each application’s components and dependencies, as well as their versions, and then report this information in a standardized SBOM format, such as CyloneDX, SPDX or SWID. Using SBOM tools, you can make SBOM generation an automated task in your CI/CD process. SBOM tools can automatically analyze each new application version or package to determine whether application components have changed, ensuring that your SBOMs are continually updated.
8. Conclusion
8.1. Software Supply Chain
The software supply chain is a complex mechanism, where an institution thats builds software products distributes code that was developed locally, as well as code written externally, by different institutions.
This mechanism is necessary, but this is also a risk for dependability and security.
8.2. Current solutions
There are many different solutions going from analyzing licenses and the social technical network of people who build those external libraries to more tool-oriented technical solutions that will run automatic dependency analysis and help you reduce the surface of the software supply chain.
References
[] Russ Cox. 2019. Surviving software dependencies. Commun. ACM 62, 9 (September 2019), 36–43. https://doi.org/10.1145/3347446
