java-topology/defects/diesel/unit/DieselTest.java
russell@unturf.com d4ed2dff91 ORM wave: 24 defects patched across 10 ORMs (157 sites, 62 ecosystems)
Hibernate (5 HIGH): addColumn/addReferencedColumn/addIndex ArrayList→LinkedHashSet (19x)
  FK second-pass LinkedHashSet, orderHierarchy LinkedHashSet
MyBatis (1 MEDIUM): sortConstructorMappings indexOf→HashMap (12x)
EF Core (2 HIGH + 1 MEDIUM): FindGenerationProperty HashSet (250x),
  AddPrincipals HashSet (250x), FK discovery HashSet (6x)
Diesel (3 MEDIUM): SQLite/MySQL row position()→BTreeMap (51x)
SQLAlchemy (2 HIGH): _values_bindparam Set (500x), evaluated_keys Set (500x)
Peewee (1 MEDIUM): _SortedFieldList.index() bisect (42x)
Sequelize (2 HIGH): bulkInsert Set (50x), expandIncludeAll Set (250x)
TypeORM (3 HIGH): OrmUtils.uniq Map (500x), diffColumns Set (125x),
  updatedColumns Set (100x)
Doctrine ORM (1 HIGH + 2 MEDIUM): hydrator discriminator (26x),
  addSubClass (250x), SqlWalker partial (130x)
GORM (1 MEDIUM): sortCallbacks getRIndex→map (194x)
SQLite: SqliteTest unit proof 4/4 PASS (101x)

Unit tests: all PASS — Hibernate/MyBatis/EfCore/Diesel/SQLAlchemy/Peewee/
  Sequelize/TypeORM/Doctrine/GORM
Whitepaper: 157 sites, 62 ecosystems; PDF 752K
2026-03-27 13:34:26 -04:00

98 lines
4.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* DieselTest — diesel-0001..0003
*
* Proves CWE-407 in Diesel (Rust ORM):
* diesel-0001: sqlite/connection/row.rs PrivateSqliteRow::Duplicated — column_names.iter().position() O(C) per named access
* diesel-0002: sqlite/connection/owned_row.rs OwnedSqliteRow — same pattern on owned variant
* diesel-0003: mysql/connection/row.rs MysqlRow — metadata.fields().iter().find() O(C) per named access
*
* Run: javac -d . DieselTest.java && java -ea unit.DieselTest
*/
public class DieselTest {
// ── diesel-0001/0002: SQLite row named column lookup ─────────────────────
/** SLOW: column_names.iter().position() O(C) per access × R rows × A accesses */
static long sqliteRowSlow(int totalCols, int colsAccessed, int numRows) {
List<String> columnNames = new ArrayList<>();
for (int c = 0; c < totalCols; c++) columnNames.add("col_" + c);
long ops = 0;
for (int r = 0; r < numRows; r++) {
for (int a = 0; a < colsAccessed; a++) {
// Access columns in order col_0, col_1, ... forcing scan up to index a
String target = "col_" + (a % totalCols);
for (int i = 0; i < columnNames.size(); i++) {
ops++;
if (columnNames.get(i).equals(target)) break;
}
}
}
return ops;
}
/** FAST: build BTreeMap index once per statement, O(1) per named access */
static long sqliteRowFast(int totalCols, int colsAccessed, int numRows) {
List<String> columnNames = new ArrayList<>();
for (int c = 0; c < totalCols; c++) columnNames.add("col_" + c);
// Build index map once
Map<String, Integer> colIndex = new TreeMap<>();
for (int i = 0; i < columnNames.size(); i++) colIndex.put(columnNames.get(i), i);
long ops = 0;
for (int r = 0; r < numRows; r++) {
for (int a = 0; a < colsAccessed; a++) {
String target = "col_" + (a % totalCols);
ops++; // O(log C) TreeMap lookup
colIndex.get(target);
}
}
return ops;
}
// ── diesel-0003: MySQL row named column lookup (same pattern) ─────────────
static long mysqlRowSlow(int totalCols, int colsAccessed, int numRows) {
return sqliteRowSlow(totalCols, colsAccessed, numRows);
}
static long mysqlRowFast(int totalCols, int colsAccessed, int numRows) {
return sqliteRowFast(totalCols, colsAccessed, numRows);
}
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 diesel-0001..0003: Diesel (Rust) CWE-407 ===");
System.out.println();
// 500 rows, each accessing 50 named columns in a 100-column result set
// Worst case: accessing columns at high indices forces long scans
final int ROWS = 500, COLS = 100, ACCESSED = 100;
long s0 = sqliteRowSlow(COLS, ACCESSED, ROWS), f0 = sqliteRowFast(COLS, ACCESSED, ROWS);
bench("diesel-0001/0002 SQLite Duplicated row position()",
() -> sqliteRowSlow(COLS, ACCESSED, ROWS), () -> sqliteRowFast(COLS, ACCESSED, ROWS), s0, f0);
long s1 = mysqlRowSlow(COLS, ACCESSED, ROWS), f1 = mysqlRowFast(COLS, ACCESSED, ROWS);
bench("diesel-0003 MySQL row fields.find()",
() -> mysqlRowSlow(COLS, ACCESSED, ROWS), () -> mysqlRowFast(COLS, ACCESSED, ROWS), s1, f1);
System.out.println();
int pass = 0;
assert s0 > f0 * 5 : "diesel-0001/0002 expected >5x"; pass++;
assert s1 > f1 * 5 : "diesel-0003 expected >5x"; pass++;
System.out.printf("%d/2 PASS — diesel-0001..0003: CWE-407 in Diesel named row access%n", pass);
System.out.printf("Hotpath: row.get(\"column_name\") — called per row per named-column field%n");
}
}