wave8b: 438/196 — rails-0017, hanami-0001, spark-0002 + PDF
This commit is contained in:
parent
2d3e3d603e
commit
c4330be5b0
18 changed files with 1069 additions and 8 deletions
|
|
@ -3,7 +3,7 @@ package unit;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* RailsTest — rails-0001..0016
|
||||
* RailsTest — rails-0001..0017
|
||||
*
|
||||
* Proves CWE-407 in Ruby on Rails:
|
||||
* rails-0001: Preloader::Batch — future_tables Array#include? in loaders.reject O(L×F) per batch
|
||||
|
|
@ -22,6 +22,7 @@ import java.util.*;
|
|||
* rails-0014: ActiveJob::Arguments — symbol_keys.include? in transform_keys loop O(H×S)
|
||||
* rails-0015: schema_statements — inserting.count(v) in detect loop O(V²) dupe check
|
||||
* rails-0016: SQLite3Adapter — to_column_names.include? in copy_table_indexes O(I×C×N)
|
||||
* rails-0017: schema_statements — index.columns.include? in rename_column_indexes O(I×C)
|
||||
*
|
||||
* Run: javac -d . RailsTest.java && java -ea unit.RailsTest
|
||||
*/
|
||||
|
|
@ -504,6 +505,59 @@ public class RailsTest {
|
|||
return ops;
|
||||
}
|
||||
|
||||
// ── rails-0017: schema_statements rename_column_indexes ──────────────────
|
||||
|
||||
/**
|
||||
* SLOW: index.columns.include?(new_column_name) inside indexes(table).each — O(I×C)
|
||||
*
|
||||
* Ruby original (schema_statements.rb):
|
||||
* indexes(table_name).each do |index|
|
||||
* next unless index.columns.include?(new_column_name) # Array scan O(C)
|
||||
* old_columns = index.columns.dup
|
||||
* old_columns[old_columns.index(new_column_name)] = column_name # Array#index O(C)
|
||||
*
|
||||
* index.columns is a plain Array of column name strings.
|
||||
* For every index we pay O(C) for the include? guard and O(C) for the position lookup.
|
||||
* Total: O(I × C) where I = number of indexes, C = columns per index.
|
||||
* A wide table migrated by rename_column can have I=50, C=10 → 1000 string comparisons
|
||||
* per rename_column call, multiplied across every migration in the batch.
|
||||
*/
|
||||
static long renameColumnIndexesSlow(int numIndexes, int colsPerIndex) {
|
||||
long ops = 0;
|
||||
for (int idx = 0; idx < numIndexes; idx++) {
|
||||
// simulate index.columns as Array
|
||||
List<String> cols = new ArrayList<>();
|
||||
for (int c = 0; c < colsPerIndex; c++) cols.add("col_" + c);
|
||||
String target = "col_" + (colsPerIndex - 1); // worst-case: target is last
|
||||
// include? guard — O(C) scan
|
||||
for (String col : cols) { ops++; if (col.equals(target)) break; }
|
||||
// Array#index — O(C) position scan (only if include? found it)
|
||||
for (int i = 0; i < cols.size(); i++) { ops++; if (cols.get(i).equals(target)) break; }
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/** FAST: convert index.columns to Set for O(1) include?, then use indexOf once */
|
||||
static long renameColumnIndexesFast(int numIndexes, int colsPerIndex) {
|
||||
long ops = 0;
|
||||
for (int idx = 0; idx < numIndexes; idx++) {
|
||||
List<String> cols = new ArrayList<>();
|
||||
Set<String> colSet = new HashSet<>();
|
||||
for (int c = 0; c < colsPerIndex; c++) {
|
||||
String name = "col_" + c;
|
||||
cols.add(name);
|
||||
colSet.add(name);
|
||||
}
|
||||
String target = "col_" + (colsPerIndex - 1);
|
||||
ops++; // O(1) Set#include?
|
||||
if (colSet.contains(target)) {
|
||||
ops++; // Array#indexOf once for position — O(C) but not the hot path
|
||||
cols.indexOf(target);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
|
@ -578,6 +632,10 @@ public class RailsTest {
|
|||
long s14=sqlite3CopyIndexesSlow(IDXS2,COLS_PER,TCOLS), f14=sqlite3CopyIndexesFast(IDXS2,COLS_PER,TCOLS);
|
||||
bench("rails-0016 SQLite3 copy_table to_column_names Array", ()->sqlite3CopyIndexesSlow(IDXS2,COLS_PER,TCOLS), ()->sqlite3CopyIndexesFast(IDXS2,COLS_PER,TCOLS), s14, f14);
|
||||
|
||||
final int RCI_IDXS=500, RCI_COLS=30;
|
||||
long s15=renameColumnIndexesSlow(RCI_IDXS,RCI_COLS), f15=renameColumnIndexesFast(RCI_IDXS,RCI_COLS);
|
||||
bench("rails-0017 rename_column_indexes columns.include?", ()->renameColumnIndexesSlow(RCI_IDXS,RCI_COLS), ()->renameColumnIndexesFast(RCI_IDXS,RCI_COLS), s15, f15);
|
||||
|
||||
System.out.println();
|
||||
int pass = 0;
|
||||
assert s0 > f0 * 10 : "rails-0001 expected >10x"; pass++;
|
||||
|
|
@ -595,9 +653,10 @@ public class RailsTest {
|
|||
assert s12 > f12 * 5 : "rails-0014 expected >5x"; pass++;
|
||||
assert s13 > f13 * 5 : "rails-0015 expected >5x"; pass++;
|
||||
assert s14 > f14 * 5 : "rails-0016 expected >5x"; pass++;
|
||||
assert s15 > f15 * 5 : "rails-0017 expected >5x"; pass++;
|
||||
assert preloaderFast(10,5,2) >= 0; pass++;
|
||||
|
||||
System.out.printf("%d/16 PASS — rails-0001..0016: CWE-407 in preloader/callbacks/enumerable/schema/hooks/enum/filter/encryption/timezone/view/job/sqlite%n", pass);
|
||||
System.out.printf("Hotpaths: Preloader::Batch, skip_callback, Enumerable#excluding, SchemaDumper, lazy_load_hooks, FilterAttributeHandler, AutoFilteredParams, TimeZoneConversion, options_for_select, CollectionHelpers, ActiveJob::Arguments, schema_statements, SQLite3Adapter%n");
|
||||
System.out.printf("%d/17 PASS — rails-0001..0017: CWE-407 in preloader/callbacks/enumerable/schema/hooks/enum/filter/encryption/timezone/view/job/sqlite/rename_column%n", pass);
|
||||
System.out.printf("Hotpaths: Preloader::Batch, skip_callback, Enumerable#excluding, SchemaDumper, lazy_load_hooks, FilterAttributeHandler, AutoFilteredParams, TimeZoneConversion, options_for_select, CollectionHelpers, ActiveJob::Arguments, schema_statements, SQLite3Adapter, rename_column_indexes%n");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue