package unit; import java.util.*; /** * PyramidTest — pyramid-0001..0005 * * Proves CWE-407 in Pyramid web framework (Python): * pyramid-0001: RoutesMapper.connect() — `in list` + list.remove() on route replace * pyramid-0002: StaticURLInfo.add() — names list rebuild + index() + pop() per registration * pyramid-0003: resolveConflicts() — list.remove(action) inside sorted output loop * pyramid-0004: TopologicalSorter.sorted() — list.pop(0)/insert(0) + `in list`/remove() * pyramid-0005: Introspector.relate()/unrelate() — list membership + removal in relation tracking * * Run: javac -d . PyramidTest.java && java -ea unit.PyramidTest */ public class PyramidTest { // ── pyramid-0001: RoutesMapper.connect() — route replace ───────────────── /** SLOW: `if oldroute in self.routelist` — O(n) scan per connect() call * Worst case: replaced route is always appended to end, so scan traverses full list */ static long routeConnectSlow(int routes, int replacements) { List routelist = new ArrayList<>(); Map routemap = new HashMap<>(); long ops = 0; // Initial population: routes 0..routes-1 for (int i = 0; i < routes; i++) { routelist.add(i); routemap.put("route_" + i, i); } // Always replace "route_0" — after first replacement it maps to a value // at the END of the list (appended), so each subsequent search is O(n) for (int r = 0; r < replacements; r++) { String name = "route_0"; Integer existing = routemap.get(name); if (existing != null) { // `if oldroute in self.routelist` — O(n) scan to end for (int x : routelist) { ops++; if (x == existing) break; } // list.remove(oldroute) — O(n) scan to end Iterator it = routelist.iterator(); while (it.hasNext()) { ops++; if (it.next().equals(existing)) { it.remove(); break; } } } int newRoute = routes + r; routelist.add(newRoute); // new route at end — next replace is O(n) routemap.put(name, newRoute); } return ops; } /** FAST: shadow set for O(1) membership check */ static long routeConnectFast(int routes, int replacements) { List routelist = new ArrayList<>(); Set routeset = new HashSet<>(); Map routemap = new HashMap<>(); long ops = 0; for (int i = 0; i < routes; i++) { routelist.add(i); routeset.add(i); routemap.put("route_" + i, i); } for (int r = 0; r < replacements; r++) { String name = "route_0"; Integer existing = routemap.get(name); if (existing != null) { ops++; // O(1) set membership if (routeset.contains(existing)) { routelist.remove(existing); routeset.remove(existing); } } int newRoute = routes + r; routelist.add(newRoute); routeset.add(newRoute); routemap.put(name, newRoute); } return ops; } // ── pyramid-0002: StaticURLInfo — names rebuild + index + pop ──────────── /** SLOW: [t[0] for t in registrations] rebuild + name in names + names.index() per add */ static long staticViewSlow(int totalViews) { // Each registration: (name, spec, route_name) List registrations = new ArrayList<>(); long ops = 0; for (int i = 0; i < totalViews; i++) { String name = "static_" + (i % (totalViews / 2)); // ~50% duplicates // Simulate: names = [t[0] for t in registrations] — O(n) rebuild List names = new ArrayList<>(); for (String[] t : registrations) { ops++; names.add(t[0]); } // if name in names — O(n) scan int idx = -1; for (int j = 0; j < names.size(); j++) { ops++; if (names.get(j).equals(name)) { idx = j; break; } } if (idx >= 0) registrations.remove(idx); registrations.add(new String[]{name, "pkg:static/" + i, "static_" + i}); } return ops; } /** FAST: persistent dict — O(1) lookup per registration, no list rebuild */ static long staticViewFast(int totalViews) { List registrations = new ArrayList<>(); Map nameIdx = new HashMap<>(); // persistent — updated on each add long ops = 0; for (int i = 0; i < totalViews; i++) { String name = "static_" + (i % (totalViews / 2)); ops++; // O(1) dict lookup — fixed Integer idx = nameIdx.get(name); if (idx != null) { registrations.remove((int)idx); // Shift all indices above idx down by 1 for (Map.Entry e : nameIdx.entrySet()) if (e.getValue() > idx) e.setValue(e.getValue()-1); } nameIdx.put(name, registrations.size()); registrations.add(new String[]{name, "pkg:static/" + i, "static_" + i}); } return ops; } // ── pyramid-0003: resolveConflicts() — list.remove in loop ─────────────── /** SLOW: remaining_actions.remove(action) — O(n) scan per resolved action */ static long actionResolveSlow(int actionCount) { List remaining = new ArrayList<>(); for (int i = 0; i < actionCount; i++) remaining.add(i); long ops = 0; // Simulate: sorted output resolves all actions in order List output = new ArrayList<>(remaining); Collections.shuffle(output, new Random(42)); for (int action : output) { // remaining_actions.remove(action) — O(n) scan Iterator it = remaining.iterator(); while (it.hasNext()) { ops++; if (it.next() == action) { it.remove(); break; } } } return ops; } /** FAST: shadow set of ids — O(1) discard per resolved action */ static long actionResolveFast(int actionCount) { List remaining = new ArrayList<>(); Set remainingSet = new HashSet<>(); for (int i = 0; i < actionCount; i++) { remaining.add(i); remainingSet.add(i); } long ops = 0; List output = new ArrayList<>(remaining); Collections.shuffle(output, new Random(42)); for (int action : output) { ops++; // O(1) set discard remainingSet.remove(action); } return ops; } // ── pyramid-0004: TopologicalSorter — list queue with pop(0)/insert(0) ─── /** SLOW: list.pop(0) + insert(0) + `in list` + list.remove() */ static long topoSortSlow(int nodeCount, int edgeCount) { List roots = new ArrayList<>(); Map> graph = new HashMap<>(); long ops = 0; for (int i = 0; i < nodeCount; i++) { roots.add(i); List adj = new ArrayList<>(); adj.add(0); // in-degree graph.put(i, adj); } Random rng = new Random(42); for (int e = 0; e < edgeCount; e++) { int from = rng.nextInt(nodeCount); int to = rng.nextInt(nodeCount); if (from == to) continue; graph.get(from).add(to); graph.get(to).set(0, graph.get(to).get(0) + 1); // `if tonode in roots` — O(n) for (int r : roots) { ops++; if (r == to) { break; } } Iterator it = roots.iterator(); while (it.hasNext()) { ops++; if (it.next() == to) { it.remove(); break; } } } List sorted = new ArrayList<>(); while (!roots.isEmpty()) { int root = roots.remove(0); // O(n) pop from front ops++; sorted.add(root); List children = graph.getOrDefault(root, List.of()).subList( 1, graph.getOrDefault(root, List.of()).size()); for (int child : children) { int arcs = graph.get(child).get(0) - 1; graph.get(child).set(0, arcs); if (arcs == 0) { roots.add(0, child); // O(n) insert at front ops++; } } } return ops; } /** FAST: ArrayDeque for O(1) popleft/appendleft + HashSet for O(1) membership */ static long topoSortFast(int nodeCount, int edgeCount) { ArrayDeque roots = new ArrayDeque<>(); Set rootsSet = new HashSet<>(); Map> graph = new HashMap<>(); long ops = 0; for (int i = 0; i < nodeCount; i++) { roots.addLast(i); rootsSet.add(i); List adj = new ArrayList<>(); adj.add(0); graph.put(i, adj); } Random rng = new Random(42); for (int e = 0; e < edgeCount; e++) { int from = rng.nextInt(nodeCount); int to = rng.nextInt(nodeCount); if (from == to) continue; graph.get(from).add(to); graph.get(to).set(0, graph.get(to).get(0) + 1); ops++; // O(1) set membership if (rootsSet.contains(to)) { roots.remove(to); rootsSet.remove(to); } } List sorted = new ArrayList<>(); while (!roots.isEmpty()) { int root = roots.pollFirst(); // O(1) ops++; rootsSet.remove(root); sorted.add(root); List full = graph.getOrDefault(root, List.of()); for (int k = 1; k < full.size(); k++) { int child = full.get(k); int arcs = graph.get(child).get(0) - 1; graph.get(child).set(0, arcs); if (arcs == 0) { roots.addFirst(child); // O(1) rootsSet.add(child); ops++; } } } return ops; } // ── pyramid-0005: Introspector.relate/unrelate — list membership ────────── /** SLOW: y not in L (list), L.remove(y) on relate/unrelate */ static long introspectorSlow(int intrCount, int relations, int unrelations) { Map> refs = new HashMap<>(); long ops = 0; Random rng = new Random(42); // relate: N² pairs, O(n) membership per pair for (int r = 0; r < relations; r++) { int x = rng.nextInt(intrCount); int y = rng.nextInt(intrCount); if (x == y) continue; List L = refs.computeIfAbsent(x, k -> new ArrayList<>()); // `y not in L` — O(n) boolean found = false; for (int v : L) { ops++; if (v == y) { found = true; break; } } if (!found) L.add(y); } // unrelate: O(n) in + remove for (int u = 0; u < unrelations; u++) { int x = rng.nextInt(intrCount); int y = rng.nextInt(intrCount); List L = refs.getOrDefault(x, new ArrayList<>()); // `if y in L` — O(n) Iterator it = L.iterator(); while (it.hasNext()) { ops++; if (it.next() == y) { it.remove(); break; } } } return ops; } /** FAST: shadow set for O(1) membership on relate/unrelate */ static long introspectorFast(int intrCount, int relations, int unrelations) { Map> refs = new HashMap<>(); Map> refsSet = new HashMap<>(); long ops = 0; Random rng = new Random(42); for (int r = 0; r < relations; r++) { int x = rng.nextInt(intrCount); int y = rng.nextInt(intrCount); if (x == y) continue; List L = refs.computeIfAbsent(x, k -> new ArrayList<>()); Set S = refsSet.computeIfAbsent(x, k -> new HashSet<>()); ops++; // O(1) set add if (S.add(y)) L.add(y); } rng = new Random(42); for (int u = 0; u < unrelations; u++) { int x = rng.nextInt(intrCount); int y = rng.nextInt(intrCount); Set S = refsSet.getOrDefault(x, new HashSet<>()); ops++; // O(1) set membership if (S.remove(y)) { refs.getOrDefault(x, new ArrayList<>()).remove(Integer.valueOf(y)); } } return ops; } static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { slow.run(); fast.run(); long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime()-t0)/1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime()-t1)/1_000_000; double r = fOps > 0 ? (double)sOps/fOps : 0; System.out.printf(" %-44s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, r); } public static void main(String[] args) { System.out.println("=== UNIT pyramid-0001..0005: Pyramid web framework CWE-407 ==="); System.out.println(); final int ROUTES = 1000, REPLACEMENTS = 4000; final int VIEWS = 2000; final int ACTIONS = 3000; final int NODES = 500, EDGES = 2000; final int INTRS = 500, RELS = 5000, UNRELS = 2000; long s0=routeConnectSlow(ROUTES,REPLACEMENTS), f0=routeConnectFast(ROUTES,REPLACEMENTS); bench("pyramid-0001 RoutesMapper.connect replace", ()->routeConnectSlow(ROUTES,REPLACEMENTS), ()->routeConnectFast(ROUTES,REPLACEMENTS), s0, f0); long s1=staticViewSlow(VIEWS), f1=staticViewFast(VIEWS); bench("pyramid-0002 StaticURLInfo names.index+pop", ()->staticViewSlow(VIEWS), ()->staticViewFast(VIEWS), s1, f1); long s2=actionResolveSlow(ACTIONS), f2=actionResolveFast(ACTIONS); bench("pyramid-0003 resolveConflicts remaining.remove", ()->actionResolveSlow(ACTIONS), ()->actionResolveFast(ACTIONS), s2, f2); long s3=topoSortSlow(NODES,EDGES), f3=topoSortFast(NODES,EDGES); bench("pyramid-0004 TopologicalSorter list queue", ()->topoSortSlow(NODES,EDGES), ()->topoSortFast(NODES,EDGES), s3, f3); long s4=introspectorSlow(INTRS,RELS,UNRELS), f4=introspectorFast(INTRS,RELS,UNRELS); bench("pyramid-0005 Introspector relate/unrelate list", ()->introspectorSlow(INTRS,RELS,UNRELS), ()->introspectorFast(INTRS,RELS,UNRELS), s4, f4); System.out.println(); int pass = 0; assert s0 > f0 * 3 : "pyramid-0001 expected >3x"; pass++; assert s1 > f1 * 3 : "pyramid-0002 expected >3x"; pass++; assert s2 > f2 * 5 : "pyramid-0003 expected >5x"; pass++; assert s3 > f3 * 3 : "pyramid-0004 expected >3x"; pass++; assert s4 > f4 * 3 : "pyramid-0005 expected >3x"; pass++; assert routeConnectFast(100,200) >= 0; pass++; System.out.printf("%d/6 PASS — pyramid-0001..0005: CWE-407 in route/view/action/sort/registry%n", pass); System.out.printf("Hotpaths: connect(), StaticURLInfo.add(), resolveConflicts(), TopologicalSorter.sorted(), Introspector%n"); } }