The first pitfall is also the most common: dynamic dependencies. These dependencies are loaded based on runtime behavior. Dynamic dependencies come in different flavors: environment dependencies, internal logic dependencies, and external data source dependencies.
Sometimes, they’re imports of packages that already exist in an interpreter or environment, like through php.ini or Python’s PYTHONSTARTUP environment variable. In other cases, they’re imports using built-in language functionality. In this case, static code analysis tools typically omit these findings. While some software composition analysis tools will find these by inspecting the entire execution environment, most will also omit such dependencies unless manually specified by the software developer. Furthermore, those SCA tools that do include the entire environment often run the risk of producing false positive results, creating fatigue for reviewers.
For example, Java applications that use ClassLoader or Python applications that use importlib to handle compatibility across different operating systems often fall into this category.
Internal Logic Dependencies
The second flavor of dynamic dependencies is more tricky. In this case, an application will dynamically install a package based on some internal logic like a requirements file or list of strings. This dynamic installation can come in many forms: source control checkouts like git clone, downloads via curl, or executing package management commands like pip or npm. In these cases, it can be very difficult for any automated tools to identify these packages.
Examples of this flavor include many larger applications that distribute “deploy scripts” or use packages that span different package managers or build systems.
External Data Source Dependencies
In the third and most extreme flavor, the dynamic dependencies to install are determined from an external data source, not internal logic. For example, the application might query an external API or request keyboard input from the user. In cases like these, it is practically impossible for static code analysis tools to identify these dependencies, and very few software composition tools will identify such dependencies either unless run on “live” applications.
Below is a simple Python example to give you an idea of how simple but problematic this pattern is. At first glance, this script seems to only use importlib, subprocess, and requests. If you ask most SCA tools, that’s what they’ll tell you too.
But in reality, an application containing code like this will install and import any packages defined in the requested URL, as you can see below. Now imagine that the URL is only accessible via an authenticated API or internal network resource, and you can see how it can be truly impossible to answer the question through static analysis or most software composition approaches.