Hidden Amplifiers: Cross-Level Risk in Software Supply Chains


Abstract

Modern software supply chains comprise hundreds of transitive dependencies, yet existing analysis tools operate at either the ecosystem level (dependency graphs) or the code level (static analysis within packages). This separation creates two failure modes. First, false-positive CVE alerts for unreachable code. Second, blind spots for structurally critical micro-dependencies. We introduce cross-level risk propagation, a framework that bridges code-level risk metrics with ecosystem-level dependency exposure through a unified risk formula. Preliminary evaluation on 50 packages across npm and PyPI reveals a class of hidden amplifiers—micro-dependencies with fewer than 50 methods but over 50,000 dependents—that carry outsized supply-chain risk invisible to all current Software Composition Analysis (SCA) tools. Without cross-level analysis, such packages can harbor exploitable code for years because no current tool considers both internal code structure and ecosystem position simultaneously. These results suggest that cross-level analysis opens a new design space for supply-chain security.

software supply chain, dependency analysis, vulnerability prioritization, software composition analysis

1 Introduction↩︎

Software supply-chain attacks have surged in both frequency and impact [@ohm2020backstabber; @ladisa2023taxonomy]. The SolarWinds compromise (2020) affected 18,000 organizations through a single build-system intrusion [@peisert2021solarwinds]. Log4Shell (2021) exposed a critical remote code execution vulnerability [@log4shell] in a logging library embedded in millions of Java applications. The event-stream incident (2018) demonstrated that a single malicious maintainer takeover of a micro-dependency can propagate to thousands of downstream projects within days [@garrett2019eventstream]. In response, governments have elevated supply-chain security to a national priority—the U.S. Executive Order 14028 mandates Software Bills of Materials (SBOMs) and supply-chain risk assessment for federal software procurement [@eo14028].

However, the scale of the problem amplifies these risks. npm alone hosts over 4 million packages, PyPI exceeds 763,000, and Maven Central surpasses 600,000—collectively serving billions of weekly downloads [@decan2019empirical]. To manage this complexity, SCA tools such as Snyk [@snyk], Dependabot [@dependabot], and npm audit have become the state of practice [@rabbi2025understanding; @abdalkareem2020impact]. These tools track known vulnerabilities at the package level, alerting developers when a dependency has a CVE. However, they treat each package as a black box—they cannot determine whether a vulnerability is reachable from the application’s code paths [@wang2026reachcheck], nor can they assess the structural importance of a dependency in the ecosystem.

This black-box limitation creates two concrete failure modes. First, false-positive overload. Many dependency-based CVE alerts target vulnerabilities in code that the consuming application never invokes, because SCA tools cannot distinguish reachable from unreachable functions. Developers learn to ignore alerts, burying genuine threats [@kula2018developers]. Second, hidden amplifier blind spots. Micro-dependencies such as ms (37 lines of JavaScript, over 829,000 dependents) carry enormous structural risk that no code-quality or SCA tool surfaces, because neither analyzes the relationship between internal code structure and ecosystem reach [@wang2026reachcheck; @mir2024effectiveness; @xu2022insight]. Zimmermann et al. [@zimmermann2019small] documented that individual npm maintainer accounts can impact up to 1 million packages, yet this risk remains invisible to current tooling.

These failures stem from a level separation. Ecosystem-level tools do not inspect code, and code-level tools do not consider ecosystem context. Consider ms, a 5-method JavaScript package with 829,914 dependents. In 2017, a ReDoS vulnerability was discovered in its core parsing function [@ms_redos]—yet no SCA tool would have flagged ms as high-risk beforehand, because it had no prior advisories and its code is trivially simple. Every dependency update, CVE alert, and transitive dependency decision is a maintenance decision, and current tools do not help developers prioritize structurally important code. A cross-level analysis would have ranked ms among the highest-risk packages based on structural position alone. We introduce cross-level risk propagation to bridge this gap. This paper makes three key contributions:

  1. A cross-level risk framework that unifies method-level code metrics with ecosystem-level dependency exposure into a single, comparable score (\(\S\) 2).

  2. A multi-layer analysis architecture that decomposes cross-level risk computation into composable stages, validated by a 16,000-LOC open-source prototype for npm and PyPI (\(\S\) 3).

  3. Preliminary evaluation results on 50 packages identifying hidden amplifiers and demonstrating that code-aware ranking substantially improves vulnerability prioritization precision over ecosystem-only approaches (\(\S\) 4).

2 Cross-Level Risk Framework↩︎

2.1 Core Insight↩︎

The risk posed by a function in a dependency is a product of two independent dimensions. The first is code-level risk—how complex, central, and reachable the function is within its own package. The second is ecosystem exposure—how many other projects transitively depend on the package. Neither dimension alone is sufficient. Only when both are elevated does a method become a genuine supply-chain concern.

2.2 Risk Model↩︎

Let \(S = \langle P, D \rangle\) be a software supply chain tuple, where \(P\) is a set of packages and \(D \subseteq P \times P\) is the transitive dependency relation. For each package \(p \in P\), let \(M(p)\) denote the set of methods extracted via static analysis, and let \(G(p) = (M(p), E(p))\) be the intra-package call graph, where \(E(p) \subseteq M(p) \times M(p)\) is the set of caller–callee edges. We define two orthogonal risk dimensions and their composition.

Definition 1 (Code-Level Risk). Code-level risk \(\varphi\colon M(p) \to \mathbb{R}_{\geq 0}\) quantifies a method’s structural importance within its package. For each \(m \in M(p)\), \(\varphi(m)\) is defined as: \[\varphi(m) = \underbrace{c(m) \cdot b(m) \cdot r(m)}_{\text{structural}} \cdot\; \underbrace{\tau(m)}_{\text{temporal}} \label{eq:composite}\qquad{(1)}\] where \(c(m)\) is cyclomatic complexity, \(b(m)\) is betweenness centrality in \(G(p)\), \(r(m)\) is blast radius (transitively reachable callees), and \(\tau(m) = \max(\log_2(1 + \mathit{churn}), 1)\) is a temporal factor from git commit frequency.

Definition 2 (Ecosystem Exposure). Ecosystem exposure \(\psi\colon P \to \mathbb{R}_{\geq 0}\) quantifies a package’s reach across the ecosystem. For each \(p \in P\), we instantiate it as \(\psi(p) = \mathrm{FanIn}(p)\), the ecosystem-wide transitive reverse-dependency count retrieved from deps.dev [@depsdev].

Neither dimension alone is sufficient to characterize supply-chain risk. A complex function in a package with zero dependents poses no supply-chain risk. A trivial getter in a massively-depended-upon package poses minimal code-level risk. We therefore compose them multiplicatively.

Definition 3 (Cross-Level Risk). The cross-level risk of method \(m \in M(p)\) is: \[\mathrm{CLR}(m) = \varphi(m) \cdot f\bigl(\psi(\mathrm{pkg}(m))\bigr) \label{eq:clr}\qquad{(2)}\] where \(f\colon \mathbb{R}_{\geq 0} \to \mathbb{R}_{\geq 0}\) is a monotone dampening function.

We choose a multiplicative form because an additive alternative would assign non-zero risk even when one dimension is absent, conflating local code concerns with supply-chain exposure. Fan-in values span six orders of magnitude (1 to 1.19M in our corpus). Without compression, a single high-fan-in package dominates all rankings. We adopt \(f(x) = \log_{10}(x + 1)\) as the default, though our sensitivity analysis (\(\S\) 4) shows the ranking is stable across alternatives (\(\rho \geq 0.97\)). One limitation is that methods with zero betweenness but high complexity may be underweighted. Exploring weighted additive alternatives using ablation study is left for future work.

2.3 Enabled Analyses↩︎

The cross-level risk model (Definitions 1–3) enables three analyses that no current tool performs.

Cross-dependency ranking. Because CLR (Def. 3) unifies code-level and ecosystem dimensions into a single score, methods from different packages can be directly compared. A method in a low-complexity but massively-depended-upon package can outrank a high-complexity method in a niche package—a comparison impossible with single-level tools.

Hidden amplifier detection. The model reveals an emergent class of high-risk packages invisible to existing tools. We define a hidden amplifier as a package \(p'\) where \(|M(p')| < \theta_M\) and \(\psi(p') > \theta_\psi\)—few methods but high ecosystem exposure. Such packages are invisible to both SCA tools (which require a known CVE) and code-quality tools (which see only simple code). In our evaluation, \(\theta_M = 50\) and \(\theta_\psi = 50{,}000\). These thresholds are configurable; the structural gap visible in Figure 2 suggests the hidden amplifier class persists across reasonable threshold choices.

The model also points toward a third capability not yet realized: reachability-aware CVE filtering, where alerts are filtered by whether vulnerable functions are reachable from the consumer’s call paths. This requires composing call graphs across package boundaries and is scoped to future work (\(\S\) 5).

2.4 Motivating Running Example↩︎

Consider a security engineer reviewing the supply chain of express@5.1.0, a popular Node.js web framework with 45 transitive dependencies. A traditional SCA tool produces CVE alerts for several dependencies, but which alerts demand immediate attention?

A code-quality tool can analyze a flagged package like qs in isolation and identify complex parsing functions—but it cannot determine that qs has 57,674 dependents, making any exploitable method a supply-chain-wide concern. Conversely, the SCA tool can rank qs by its fan-in, but all methods within qs receive the same score, providing no guidance on which functions to audit first.

With cross-level analysis, the engineer can see that readStream() in raw-body scores CLR\(=\)​40.0 (high code complexity \(\times\) significant fan-in), while compile() in proxy-addr (CR\(=\)​3.93) ranks nearly as high (CLR\(=\)​21.3) despite lower code-level risk, because proxy-addr has 261,831 dependents—\(4.5\times\) wider ecosystem reach. The cross-level formula correctly amplifies scores across the ecosystem dimension. Table 1 summarizes the top-ranked methods. None of these cross-dependency comparisons are possible with existing tools.

Table 1: Top cross-level risk methods in the express@5.1.0 supply chain. CR\(=\)code-level risk (Def. 1), FI\(=\)fan-in (ecosystem exposure, sourced from deps.dev).
Method Package CR FI CLR
readStream() raw-body 8.33 63,898 40.0
typeis() type-is 5.87 59,995 28.0
next() router 4.48 57,674 21.3
compile() proxy-addr 3.93 261,831 21.3
alladdrs() proxy-addr 2.91 261,831 15.8

3 Architecture and Implementation↩︎

Computing CLR (Def. 3) requires combining data from two independent sources—intra-package call graphs for code-level risk (Def. 1) and ecosystem-wide dependency metadata for ecosystem exposure (Def. 2). However, no existing tool produces both. We decompose the analysis into three composable layers, illustrated in Figure 1.

Layer 1: Ecosystem Analysis computes ecosystem exposure \(\psi(p)\) (Def. 2) for a package \(p\). Given a root package, the framework resolves its full transitive dependency tree via registry APIs (npm, PyPI) and enriches each node with fan-in counts from deps.dev [@depsdev], along with supplementary health indicators—OpenSSF Scorecard [@openssf] scores, libyears freshness [@cox2015measuring], and known CVEs from the OSV database [@osv]—that inform practitioner triage but are not inputs to the CLR formula. The output is a fully attributed dependency graph.

Layer 2: Code Analysis computes code-level risk \(\varphi(m)\) (Def. 1) for each method \(m \in M(p)\). For each dependency, the framework clones the source, constructs an AST-based static call graph \(G(p)\) using language-specific walkers, and computes per-method risk via Eq. ?? . A novel internal resolution rate quantifies call-graph completeness. \[R = \frac{|\text{resolved internal call sites}|}{|\text{total internal call sites}|} \label{eq:resolution}\tag{1}\] where internal excludes external and dynamic calls. \(R\) serves as a confidence gate. Packages with \(R < 0.85\) produce unreliable call graphs, and the framework flags their results accordingly.

Layer 3: Cross-Level Synthesis computes CLR scores (Def. 3) for every method across the dependency tree. Dependencies are first ranked by a bottleneck score—defined as \(\text{in-degree}(p) \times \text{out-degree}(p)\) within the dependency tree—which identifies packages that mediate the most transitive paths. The top-\(N\) bottleneck packages (default \(N{=}20\)) are selected for method-level analysis, and the resulting per-method risks are unified into a single cross-package ranking.

A prototype implementing this framework (approximately 16,000 lines of Python and TypeScript) is publicly available, along with a replication package containing the corpus definition, analysis scripts, and exported datasets.1

Figure 1: Cross-level analysis workflow. Layer 1 resolvesdependencies and retrieves ecosystem exposure \psi(p).Layer 2 constructs call graphs and computes code-levelrisk \varphi(m). Layer 3 synthesises both dimensionsvia Eq. ?? . The dashed bypass shows \psi(p)flowing directly from L1 to L3.

4 Evaluation↩︎

We evaluated the prototype on a corpus of 50 packages across npm (35) and PyPI (15). Packages were drawn from the most depended-upon libraries in each registry and stratified into three archetypes. High-impact frameworks (e.g., express, flask), security-critical libraries (e.g., axios, urllib3), and micro-dependencies (e.g., ms, inherits). This curated selection may overrepresent well-maintained packages. A random sample from the full registry would provide a more representative baseline. The corpus includes 10 root packages whose dependency trees contain known CVEs, enabling precision evaluation. The cross-level pipeline produced 825 method-risk entries across all analyzed packages.

For each research question below, we present the motivation, approach, and key findings.

4.1 RQ1: Do hidden amplifiers form a structurally distinct class invisible to current SCA tools?↩︎

4.1.0.1 Motivation

Micro-dependencies are invisible to current SCA tools because they have no prior CVEs and their code is trivially simple. If such packages carry outsized structural risk due to their ecosystem position, a new detection mechanism is needed.

4.1.0.2 Approach

We apply the hidden amplifier thresholds (\(|M(p)| < 50\), \(\psi(p) > 50{,}000\)) to the 50-package corpus and validate with a retrospective analysis of the ms package.

4.1.0.3 Results

Our primary finding is the systematic identification of hidden amplifiers—micro-dependencies that score high in cross-level risk despite trivial internal complexity. Of the 50 packages in our corpus, 12 (24%) qualify as hidden amplifiers (\(|M(p)| < 50\) and \(\psi(p) > 50{,}000\)). This rate reflects our selection of high-fan-in packages. In a random registry sample the prevalence would be much lower. The significant finding is not the rate itself, but that these packages form a structurally distinct class visible only through cross-level analysis (Figure 2). Table 2 lists the top examples.

Table 2: Hidden amplifiers: micro-dependencies with \(<\)50 methods but \(>\)50,000 dependents. No current SCA tool flags these without a known CVE.
Package Methods Fan-in LOC
inherits 3 1,187,337 23
ms 5 829,914 105
ee-first 6 334,743 94
escape-html 1 327,489 46
depd 32 321,932 493
utils-merge 1 227,359 8
fresh 3 60,295 89

inherits, a 23-line package with 1.19 million dependents and only 3 methods, yields CLR \(\approx 12.1\) even with moderate code-level risk. The logarithmic dampening compresses the fan-in advantage: a method in raw-body with \(4.2\times\) higher code-level risk scores only \(3.3\times\) higher CLR. Figure 2 visualizes this: hidden amplifiers cluster in the bottom-right quadrant, forming a structurally distinct class that no single-dimension tool can identify.

Figure 2: Method count vs.fan-in across 50 corpus packages (log-log). The 12 red points in the bottom-right quadrant (<​50 methods, >​50K dependents) are hidden amplifiers—all listed or characterized in Table 2.

Retrospective validation. The case of ms provides a natural experiment. In 2017, a ReDoS vulnerability was discovered in its core parse() function (fixed in v2.0.0). No SCA tool flagged ms as high-risk before the advisory—it had no prior CVEs and its code is trivially simple. Our framework, applied retroactively, would have ranked ms as a hidden amplifier based solely on structural position.2 This shows that hidden amplifier detection identifies a class of risk that is invisible to current practice.

4.2 RQ2: Does cross-level scoring produce meaningfully different rankings and improve prioritization?↩︎

4.2.0.1 Motivation

If cross-level scoring does not produce meaningfully different rankings from single-dimension approaches, the additional complexity of combining code-level and ecosystem-level analysis offers no practical value.

4.2.0.2 Approach

We evaluate ranking quality using Precision@\(K\) (P@\(K\))—the fraction of top-\(K\) ranked methods that belong to a dependency with a documented vulnerability—across the 10 root packages with known CVEs. We compare three scoring strategies: CLR (cross-level), CR (code-only), and FI (fan-in only). We present these results as a feasibility demonstration. The sample size precludes statistical generalization.

Table 3: Precision@\(K\) across 10 CVE-bearing root packages (\(n{=}10\), feasibility only). CLR\(=\)cross-level, CR\(=\)code-only, FI\(=\)fan-in only.
P@3 P@5 P@10
CLR (cross-level) 0.43 0.44 0.38
CR (code-only) 0.50 0.44 0.42
FI (fan-in only) 0.20 0.20 0.23

4.2.0.3 Results

The ranking evaluation reveals two distinct capabilities. First, code awareness improves precision: both CLR and CR approximately double ecosystem-only precision across all \(K\) values (Table 3). Incorporating code-level metrics improves vulnerability prioritization over fan-in alone. This improvement is shared by CLR and CR because fan-in cannot discriminate between methods within the same dependency.

Second, CLR enables cross-dependency comparison—a capability CR lacks entirely. CR can only rank methods within a package. CLR produces a single scale across packages. CR matches or exceeds CLR on per-root P@\(K\) because within a single dependency tree, code-level variation alone is sufficient to discriminate; CR’s advantage on this metric does not extend to cross-tree comparison, which CR cannot perform at all. CLR’s added value is precisely this cross-project ranking.

The re-ordering is substantial as 24.5% of methods change rank position when moving from CR to CLR. Ecosystem exposure materially changes prioritization. Evaluating whether this re-ordering correlates with actual vulnerability outcomes requires a larger corpus (\(\S\) 5).

Testing four dampening functions (\(\log_{10}\), \(\log_2\), \(\sqrt{}\), linear) and computing Spearman’s rank correlation coefficient (\(\rho\)) between the resulting rankings yields \(\rho \geq 0.97\) against the default \(\log_{10}\). Practitioners can select any variant without sacrificing ranking quality.

4.3 RQ3: How reliable are the data recovery and static analysis components of the pipeline?↩︎

4.3.0.1 Motivation

The framework’s outputs are only useful if the underlying data recovery and static analysis components produce reliable results. We assess the accuracy of our patch mining technique and the quality of the generated call graphs.

4.3.0.2 Approach

We evaluate two pipeline components. First, patch mining—an automated technique that extracts affected function names from vulnerability fix commit diffs via the GitHub API—is assessed for precision on 45 advisories. Second, call-graph quality is measured via the internal resolution rate \(R\) (Eq. 1 ) across the 50-package corpus.

4.3.0.3 Results

For patch mining, 31 of 45 advisories (69%) yielded function names at 84% precision. All 15 mined functions found in our call graphs are reachable from the package’s public API, confirming these CVEs affect actively used code.

For call-graph quality, the internal resolution rate (\(R\), Eq. 1 ) provides a transparency signal. Figure 3 shows the distribution across our 50-package corpus. The distribution is heavily right-skewed: 70% of packages achieve \(R \geq 0.85\) (suitable for reachability analysis), while a long tail of 20% fall below \(R = 0.60\) (dynamic dispatch). Reporting \(R\) alongside risk scores enables practitioners to assess result confidence—epistemic transparency absent from existing tools.

Figure 3: Distribution of internal resolution rate (R) across 50 packages. 70% achieve R \geq 0.85. The right-skewed shape reflects a clear divide between statically analyzable and dynamic-dispatch-heavy packages.

5 Discussion↩︎

Our results demonstrate feasibility but also reveal limitations and future directions.

5.0.0.1 Full-graph reachability

Our pipeline exports only the top-\(N{=}20\) methods per package. Full call-graph export would enable end-to-end reachability filtering. Early results confirm CVE-affected functions do not cluster in the top-20 (function-level P@5 \(= 0.029\)). This is an essential next step.

5.0.0.2 Cross-package call-graph composition

The current reachability scope is intra-dependency. Composing call graphs across package boundaries would enable consumer-specific verdicts [@pashchenko2020qualitative], but requires solving inter-package name resolution—a harder problem due to dynamic imports.

5.0.0.3 Ecosystem-scale deployment

Our full-clone analysis processes \(\sim\)​3 packages per minute. Scaling to entire registries requires incremental analysis and call-graph caches.

5.0.0.4 Threats to validity

Construct: The multiplicative CLR model assumes risk is zero when either dimension is absent. Internal: Our curated corpus overrepresents well-maintained, high-fan-in packages. External: Results are limited to npm and PyPI. Reliability: Fan-in counts are point-in-time snapshots. \(R\) quantifies the static analysis limitation transparently.

6 Related Work↩︎

6.0.0.1 SCA Tools

Production tools (Snyk [@snyk], Dependabot [@dependabot], Renovate [@renovate]) operate at the package level, surfacing CVEs via version matching without inspecting code or reachability. npm audit on express@5.1.0 produces zero alerts for hidden amplifiers such as ms or inherits. Table ¿tbl:tab:comparison? shows cross-level analysis against existing approaches.

@lcccc@ Approach & Code & Eco. & Reach. & Cross
SCA tools [@snyk; @dependabot; @renovate] & — & ✔ & — & —
Code-quality (e.g., SonarQube) & ✔ & — & — & —
Eclipse Steady [@plate2015eclipsesteady] & ✔ & — & ✔\(^*\) & —
PyCG [@salis2021pycg] & ✔ & — & — & —
Our framework & ✔ & ✔ & ✔ & ✔

6.0.0.2 Reachability Analysis

Eclipse Steady [@plate2015eclipsesteady] performs dynamic reachability for Java via bytecode instrumentation—the closest prior work. Our framework differs by using static analysis (no test execution required), adding ecosystem-level fan-in scaling, and targeting npm/PyPI. Ponta et al. [@ponta2018beyond; @ponta2019projectkb] introduced the “vulnerable construct” concept. Our patch mining automates this from fix commit diffs.

6.0.0.3 Ecosystem Analysis

Decan et al. [@decan2019empirical] compared dependency network evolution across seven ecosystems. Zimmermann et al. [@zimmermann2019small] quantified the blast radius of maintainer compromises in npm. Liu et al. [@liu2022demystifying] traced vulnerability propagation through dependency trees. These works analyze dependency graph structure but do not inspect code within packages—the gap our approach bridges.

6.0.0.4 Static Analysis and Health Scoring

PyCG [@salis2021pycg] constructs call graphs for Python. Dann et al. [@dann2022modguard] identified challenges for OSS vulnerability scanners. Our prototype adds the resolution rate as a confidence metric absent from prior work. The OpenSSF Scorecard [@openssf] assigns health scores based on maintenance practices but does not inspect code architecture. Our approach evaluates whether code poses structural risk rather than whether a project follows good practices.

7 Conclusion↩︎

We have introduced cross-level risk propagation, a framework that unifies method-level code metrics with ecosystem-level dependency exposure into comparable risk scores. Evaluation on 50 packages reveals hidden amplifiers invisible to current SCA tools and shows that code-aware ranking substantially improves prioritization precision over ecosystem-only approaches. The retrospective case of ms validates that structural risk signals can provide early warning before disclosure. These results suggest that the current separation between code-quality tools and ecosystem-level SCA is a design limitation, not an inherent boundary—bridging it is both feasible and practically valuable.

Acknowledgment↩︎

GitHub Copilot and Claude (Anthropic) assisted with code completion during prototype development (\(\S\)3) as coding assistance. All research direction, methodology, and interpretation are the intellectual contributions of the human authors.


  1. Dependency Observatory — https://github.com/fabgonzalez-dev/oscar-dependency-observatory, Method Observatory — https://github.com/fabgonzalez-dev/oscar-method-observatory, Frontend — https://github.com/fabgonzalez-dev/oscar-frontend, Research Data — https://github.com/fabgonzalez-dev/oscar-research-data.↩︎

  2. deps.dev does not provide historical fan-in snapshots. However, npm contained approximately 350,000 packages in early 2017 vs.over 4 million today. Proportional scaling yields a conservative 2017 fan-in estimate of \(\sim\)​73,000 for ms—well above the 50,000 hidden amplifier threshold. With \(\varphi \approx 4.0\) for its parse() function, this produces CLR \(\approx 4.0 \times \log_{10}(73{,}001) \approx 19.5\), ranking in the top 10 methods across any dependency tree containing ms.↩︎