package unit; import java.util.*; /** * TfAwsCloudFormationAlgorithm — CWE-407 benchmark * * Models terraform-provider-aws cloudformation/stack_set_instance.go * findStackInstanceSummariesByFourPartKey(): * SLOW: slices.Contains(orgIDs, v.OrganizationalUnitId) — O(O) per summary * called S times inside pagination → O(S × O) total * FAST: pre-build map[string]bool from orgIDs → O(1) per summary * * tf-aws-0001 */ public class TfAwsCloudFormationAlgorithm { // ------------------------------------------------------------------ nodes static class Node { String ouId; String stackInstanceId; Node(String ouId, String stackInstanceId) { this.ouId = ouId; this.stackInstanceId = stackInstanceId; } } // ------------------------------------------------------------------ result static class Result { List matched; Result(List matched) { this.matched = matched; } } // ------------------------------------------------------------------ slow (defect) static class DefectiveFinder { /** * For each summary across all pagination pages, scan orgIDs linearly. * O(S × O) where S = total summaries, O = number of org IDs. */ static Result findSummaries(List> pages, List orgIDs) { List output = new ArrayList<>(); for (List page : pages) { for (Node v : page) { // CWE-407: linear scan of orgIDs on every summary if (orgIDs.contains(v.ouId)) { // O(O) output.add(v); } } } return new Result(output); } } // ------------------------------------------------------------------ fast (fix) static class FixedFinder { /** * Pre-build a HashSet from orgIDs before the pagination loop. * O(S + O) total. */ static Result findSummaries(List> pages, List orgIDs) { // CWE-407 fix: O(1) lookup set built once before pagination Set orgIDSet = new HashSet<>(orgIDs); List output = new ArrayList<>(); for (List page : pages) { for (Node v : page) { if (orgIDSet.contains(v.ouId)) { // O(1) output.add(v); } } } return new Result(output); } } // ------------------------------------------------------------------ helpers static List> buildPages(int totalSummaries, int pageSize, int numOUs) { List> pages = new ArrayList<>(); List page = new ArrayList<>(); for (int i = 0; i < totalSummaries; i++) { String ouId = "ou-" + (i % numOUs); // distribute across OUs page.add(new Node(ouId, "stack-instance-" + i)); if (page.size() == pageSize) { pages.add(page); page = new ArrayList<>(); } } if (!page.isEmpty()) pages.add(page); return pages; } static List buildOrgIDs(int n) { List ids = new ArrayList<>(n); for (int i = 0; i < n; i++) { ids.add("ou-" + i); } return ids; } static long benchSlow(int S, int O, int iters) { List> pages = buildPages(S, 100, O * 2); List orgIDs = buildOrgIDs(O); long start = System.nanoTime(); for (int i = 0; i < iters; i++) { DefectiveFinder.findSummaries(pages, orgIDs); } return System.nanoTime() - start; } static long benchFast(int S, int O, int iters) { List> pages = buildPages(S, 100, O * 2); List orgIDs = buildOrgIDs(O); long start = System.nanoTime(); for (int i = 0; i < iters; i++) { FixedFinder.findSummaries(pages, orgIDs); } return System.nanoTime() - start; } // ------------------------------------------------------------------ main public static void main(String[] args) { int passed = 0, total = 0; // ---- correctness List page1 = Arrays.asList( new Node("ou-1", "s1"), new Node("ou-2", "s2"), new Node("ou-3", "s3") ); List page2 = Arrays.asList( new Node("ou-4", "s4"), new Node("ou-1", "s5") ); List> pages = Arrays.asList(page1, page2); List orgIDs = Arrays.asList("ou-1", "ou-3"); Result slowR = DefectiveFinder.findSummaries(pages, orgIDs); Result fastR = FixedFinder.findSummaries(pages, orgIDs); assert slowR.matched.size() == 3 : "slow: expected 3 matches, got " + slowR.matched.size(); assert fastR.matched.size() == 3 : "fast: expected 3 matches, got " + fastR.matched.size(); assert slowR.matched.size() == fastR.matched.size() : "slow/fast count mismatch"; System.out.println("Correctness: PASS (slow.matched.size == fast.matched.size == 3)"); // ---- performance int ITERS = 50; int[][] scenarios = {{2000, 200}, {4000, 350}, {7000, 500}}; System.out.printf("%-6s %-6s %-12s %-12s %s%n", "S", "O", "slow(ns)", "fast(ns)", "ratio"); for (int[] sc : scenarios) { int S = sc[0], O = sc[1]; // warm-up (extra iterations to stabilize JIT at larger sizes) benchSlow(S, O, 10); benchFast(S, O, 10); long slowNs = benchSlow(S, O, ITERS); long fastNs = benchFast(S, O, ITERS); double ratio = (double) slowNs / fastNs; System.out.printf("%-6d %-6d %-12d %-12d %.2fx%n", S, O, slowNs, fastNs, ratio); total++; if (ratio >= 5.0) { System.out.printf(" PASS (ratio=%.2f >= 5.0)%n", ratio); passed++; } else { System.out.printf(" FAIL (ratio=%.2f < 5.0)%n", ratio); } } System.out.printf("%nTests: %d/%d PASS%n", passed, total); if (passed < total) System.exit(1); } }