package unit; import java.util.*; /** * SequelizeTest — sequelize-0001..0002 * * Proves CWE-407 in Sequelize ORM: * sequelize-0001: query-generator.js bulkInsertQuery — allAttributes.includes() O(C) in double loop * O(rows × cols²) for bulk INSERT with many rows and columns * sequelize-0002: model.js _expandIncludeAll — all.includes(type_) O(T) inside for-of loop * O(T²) when expanding association types * * Run: javac -d . SequelizeTest.java && java -ea unit.SequelizeTest */ public class SequelizeTest { // ── sequelize-0001: bulkInsertQuery allAttributes dedup ────────────────── /** * SLOW: allAttributes.includes(key) O(C) inside double loop (rows × cols). * allAttributes grows incrementally; each new key requires O(C) scan. * O(rows × cols × C) total where C = unique cols seen so far. */ static long bulkInsertSlow(int numRows, int numCols) { List allAttributes = new ArrayList<>(); long ops = 0; for (int r = 0; r < numRows; r++) { for (int c = 0; c < numCols; c++) { String key = "col_" + c; // allAttributes.includes(key) boolean found = false; for (String a : allAttributes) { ops++; if (a.equals(key)) { found = true; break; } } if (!found) allAttributes.add(key); } } return ops; } /** FAST: allAttributesSet.has(key) O(1) — shadow Set tracks already-seen cols. */ static long bulkInsertFast(int numRows, int numCols) { Set allAttributesSet = new HashSet<>(); List allAttributes = new ArrayList<>(); long ops = 0; for (int r = 0; r < numRows; r++) { for (int c = 0; c < numCols; c++) { String key = "col_" + c; ops++; // O(1) set.has if (allAttributesSet.add(key)) allAttributes.add(key); } } return ops; } // ── sequelize-0002: _expandIncludeAll type dedup ───────────────────────── /** * SLOW: all.includes(type_) O(T) per expansion step. * all grows as types are added; membership test is O(T) per type. * O(T²) total for T association types. */ static long expandIncludeAllSlow(int numTypes) { List all = new ArrayList<>(); long ops = 0; for (int i = 0; i < numTypes; i++) { String type = "type_" + i; // all.includes(type_) boolean found = false; for (String a : all) { ops++; if (a.equals(type)) { found = true; break; } } if (!found) { all.add(0, type); } // unshift — O(T) } return ops; } /** FAST: allSet.has(type_) O(1) with shadow Set; O(T) total. */ static long expandIncludeAllFast(int numTypes) { Set allSet = new HashSet<>(); List all = new ArrayList<>(); long ops = 0; for (int i = 0; i < numTypes; i++) { String type = "type_" + i; ops++; // O(1) if (allSet.add(type)) all.add(0, type); } 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(" %-52s 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 sequelize-0001..0002: Sequelize CWE-407 ==="); System.out.println(); final int ROWS = 500, COLS = 100; // sequelize-0001 final int TYPES = 500; // sequelize-0002 long s0 = bulkInsertSlow(ROWS, COLS), f0 = bulkInsertFast(ROWS, COLS); bench("sequelize-0001 bulkInsert allAttributes.includes()", () -> bulkInsertSlow(ROWS, COLS), () -> bulkInsertFast(ROWS, COLS), s0, f0); long s1 = expandIncludeAllSlow(TYPES), f1 = expandIncludeAllFast(TYPES); bench("sequelize-0002 expandIncludeAll all.includes(type_)", () -> expandIncludeAllSlow(TYPES), () -> expandIncludeAllFast(TYPES), s1, f1); System.out.println(); int pass = 0; assert s0 > f0 * 5 : "sequelize-0001 expected >5x"; pass++; assert s1 > f1 * 5 : "sequelize-0002 expected >5x"; pass++; System.out.printf("%d/2 PASS — sequelize-0001..0002: CWE-407 in Sequelize query builder/model%n", pass); System.out.printf("Hotpaths: Model.bulkCreate() with many rows, Model.findAll() with include: 'all'%n"); } }