Let’s be honest – there are no silver bullets when it comes to Software Composition Analysis (SCA). Both manifest scanners (a.k.a. static SCA) and runtime tools (a.k.a. dynamic SCA) claim they’re superior to their counterpart, but neither seem to have a clear hold on which is actually better. This is primarily because each approach’s pros and cons counterbalance each other, resulting in an unclear victor. In this article, we’ll walk through how these approaches differ, the pros and cons of each, and how Endor Labs is solving the problem.
Static SCA vs. Dynamic SCA: What are the Differences?
An application and its vulnerabilities can be thought of as a map composed of roads and shortcuts, with a road equating to the intended functionality in the application and a shortcut being a bridge between roads that leads to unintended functionality and access. In this metaphor, software vulnerability analysis can be thought of as the process of identifying the shortcuts in an application’s map. When assessing the security posture of an application, SCA tools generally focus on just one of two key metrics to deliver that map: Either completeness or relevance.
Pros and Cons of Static SCA
Static SCA is when an application’s source code, including its third-party dependencies, is scanned during the build phase for security vulnerabilities. Examples of static SCA vendors include Snyk, Black Duck, and Veracode.
Benefits of Static SCA: Completeness and Proactivity
Static SCA focuses on the completeness of a security assessment relating to the totality of the vulnerabilities found in the application software – its aim is to find all potential vulnerabilities. Depending on the languages in use, this approach can illuminate all roads (i.e. functionality) and shortcuts (i.e. vulnerabilities) on a map. This approach also allows teams to be proactive, in that you can identify vulnerabilities at the pre-commit phase rather than waiting until it’s ready for release.
Problems with Static SCA: Inaccuracy and Noise
Most static SCA approaches rely solely on manifest files rather than the actual application source code. This is problematic for languages (like Python) that don’t consistently include all dependencies in the manifest. In this scenario, the SCA tool fails to identify artifacts that contain vulnerable code because it can’t find dependencies that aren’t in the manifest. This limits the utility of the SCA tool while simultaneously providing your organization with a false sense of security.
The second problem (caused by the goal of producing complete results) is the SCA tool wrongly thinks an artifact contains vulnerable code because it surfaces unused dependencies based on the manifest. This produces an overwhelming volume of results that lack context - which we call “noise” - and leaves the user clueless as to which shortcuts matter the most on their application’s map. The AppSec team has no way of differentiating noise from true risk, so engineering is stuck with tracking down whether a package is used and justifying the findings.
Pros and Cons of Dynamic SCA
Dynamic SCA is the process in which an application’s binary is instrumented at runtime, either in a testing or production environment, to observe its APIs and their parameters to detect security vulnerabilities in real-time. This approach only illuminates the roads and shortcuts observed during the runtime. A host of startups are staking their futures on dynamic SCA because if you can really get it right, there’s tremendous potential.
Benefit of Dynamic SCA: Relevance
Dynamic SCA tools prioritize the relevance of a security assessment relating to the likelihood that the vulnerability can be exploited at runtime – its aim is to determine how important a vulnerability is to an application’s security posture. Because dynamic SCA provides a list of vulnerabilities from code exercised at runtime, it reduces noise by giving you assurance that the vulnerabilities discovered are accessible.
Problems with Dynamic SCA: Incompleteness, Performance Tax, and Delays
It’s rare for test coverage to reach 100% so the list of vulnerabilities will be incomplete because the tool only provides insight into what has been seen. Continuing with our mapping metaphor, dynamic SCA creates an incomplete map that leaves a user to guess how many other roads and shortcuts exist in their application that can be discovered by an attacker. This out of sight, out of mind creates a dangerous false sense of security. Dynamic SCA tools require an agent, which can be a dealbreaker due to performance impacts on the application. Further, because the scan happens close to the time of release, the probability of breaking a build or delaying a release is much higher than when you assess risks throughout the SDLC.
Has eBPF Fixed Dynamic SCA?
Extended Berkeley Packet Filter (eBPF) is an extension of the original BPF that enables the tracing of Linux applications via the kernel without having to restart the application. eBPF has unlocked new runtime monitoring capabilities for modern Linux applications including SCA-related information. eBPF has been touted as a progression for dynamic SCA primarily because of its ability to observe an application without the need to instrument it directly. Despite eBPF’s enhancements, the primary drawback remains the same as every dynamic approach – it can only observe what is seen at runtime. eBPF enhances the deployability of dynamic SCA approaches, but does not target the core issue: Being limited to what is seen during the runtime cannot guarantee what isn’t seen in the rest of the application map.
Example of Static and Dynamic SCA Results
Let’s take a deeper dive to see the different results produced by static and dynamic SCA tools. This example is simplistic in nature to illustrate the main differences between the two approaches.
We start by assuming SCA is being run for the following code. The main_function uses lib_func_1 and lib_func_2 functions from from library foo.
Library foo implements N functions shown in this snippet.
In the foo library code, let’s assume the library functions use vulnerable functions, arbitrarily numbered 1 to N, that result in SCA findings.
With a static approach, the results include the use of all the vulnerable function calls found in library foo despite the main_function only using code from 2 of the functions.
Static SCA Results
With a dynamic approach, the code must be exercised in order to generate results. Let’s assume the following test code is being used:
With this test code, only the first if statement of the main_function is executed. Although in this example it is trivial to get 100% code coverage due to the simplicity of the code, in real applications 100% coverage is oftentimes infeasible and impractical. The results from a dynamic approach are more relevant, but in a real life scenario the list of findings is incomplete and thus misleading.
Dynamic SCA Results
This is the Way: Static SCA with Reachability Analysis
With a seemingly lose-lose situation, where does SCA go from here?
Endor Labs posits that both attributes (completeness and relevance) must to be equally prioritized to provide a complete SCA solution. We start with a static approach’s access to the application’s source code (not just the manifest) to illuminate the entire application map. Afterall, a security assessment cannot be accurate without being complete. Then we apply reachability analysis on the source code to provide users with the ability to filter issues based on whether your code is calling the vulnerable code.This reduces noise of irrelevant issues, letting you focus on the vulnerabilities that matter.
Watch this short tutorial to see reachability analysis in practice: