package unit; import java.util.*; /** * Standalone unit test for helm CWE-407 defect. * * helm-0001: processDependencyEnabled — O(n²) nested dependency lookup * Pattern A: for each existing dep, scan all metadata deps — O(E × M). * Pattern B: getAliasDependency called per metadata dep — O(M × C). * * slow() counts ops for both patterns with nested loops. * fast() counts ops using a pre-built name→entry map for O(1) lookups. * Assert: slowOps > fastOps * 5x for D=200 dependencies. */ public class HelmTest { static class ChartDep { String name; String version; ChartDep(String name, String version) { this.name = name; this.version = version; } } /** * Slow path — Pattern A: O(existing × metaDeps). * Pattern B: O(metaDeps × charts) where getAliasDependency scans charts linearly. */ static long slowProcessDependencies(List existing, List metaDeps) { long ops = 0; // Pattern A: filter existing not in metaDeps List chartDeps = new ArrayList<>(); outer: for (ChartDep ex : existing) { for (ChartDep req : metaDeps) { // O(M) per existing item ops++; if (ex.name.equals(req.name)) { continue outer; } } chartDeps.add(ex); } // Pattern B: for each metaDep, scan existing (getAliasDependency linear scan) for (ChartDep req : metaDeps) { for (ChartDep ch : existing) { // O(C) per metaDep ops++; if (ch.name.equals(req.name)) { chartDeps.add(ch); // alias copy break; } } } return ops; } /** * Fast path — build name→ChartDep maps once; O(1) lookups. */ static long fastProcessDependencies(List existing, List metaDeps) { long ops = 0; // Build index: O(E) + O(M) Map metaByName = new HashMap<>(metaDeps.size()); for (ChartDep req : metaDeps) { ops++; metaByName.put(req.name, req); } Map chartsByName = new HashMap<>(existing.size()); for (ChartDep ch : existing) { ops++; chartsByName.put(ch.name, ch); } // Pattern A replacement — O(E) with O(1) lookup List chartDeps = new ArrayList<>(); for (ChartDep ex : existing) { ops++; if (!metaByName.containsKey(ex.name)) { chartDeps.add(ex); } } // Pattern B replacement — O(M) with O(1) lookup for (ChartDep req : metaDeps) { ops++; ChartDep ch = chartsByName.get(req.name); if (ch != null) { chartDeps.add(ch); } } return ops; } static void testProcessDependencies() { int D = 200; // number of dependencies List existing = new ArrayList<>(D); List metaDeps = new ArrayList<>(D); for (int i = 0; i < D; i++) { existing.add(new ChartDep("chart-" + i, "1.0." + i)); metaDeps.add(new ChartDep("chart-" + i, ">=1.0.0")); } long sOps = slowProcessDependencies(existing, metaDeps); long fOps = fastProcessDependencies(existing, metaDeps); int Nx = 5; boolean pass = sOps > fOps * Nx; System.out.printf("helm-0001 [D=%d]: slow=%d fast=%d ratio=%.1fx — %s%n", D, sOps, fOps, (double) sOps / fOps, pass ? "PASS" : "FAIL"); if (!pass) throw new AssertionError("helm-0001 FAIL: slow=" + sOps + " fast=" + fOps); } public static void main(String[] args) { testProcessDependencies(); System.out.println("1/1 PASS"); } }