whitepaper: 398/185 — wave6d (rails-0012..16, jsc-0001/2, vtk, sm-0002, redis/valkey-0003, helm-0002/3, k8s-0003)

This commit is contained in:
russell@unturf.com 2026-03-27 15:57:50 -04:00
parent eb9612e4bf
commit 3735145aa5
47 changed files with 3488 additions and 33 deletions

View file

@ -3,7 +3,7 @@ package unit;
import java.util.*;
/**
* RailsTest rails-0001..0008
* RailsTest rails-0001..0016
*
* Proves CWE-407 in Ruby on Rails:
* rails-0001: Preloader::Batch future_tables Array#include? in loaders.reject O(L×F) per batch
@ -14,6 +14,14 @@ import java.util.*;
* rails-0006: PostgreSQL schema_statements include_columns Array#include? in reject O(C×I)
* rails-0007: lazy_load_hooks @run_once[name].include?(block) Array scan per hook O(H×R)
* rails-0008: Enum value_method_names.include? in pairs.each loop O(E²)
* rails-0009: FilterAttributeHandler filter_params.include? in each loop O(A×F)
* rails-0010: Encryption::AutoFilteredParams filter_parameters.include? in each O(A×F)
* rails-0011: TimeZoneConversion skip_list.include? per column per model O(M×C×S)
* rails-0012: options_for_select Array(selected).include? in container.map O(N×S)
* rails-0013: CollectionHelpers Array(current_value).include? in render_collection O(C×V×4)
* 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)
*
* Run: javac -d . RailsTest.java && java -ea unit.RailsTest
*/
@ -340,6 +348,162 @@ public class RailsTest {
return ops;
}
// rails-0012: options_for_select selected/disabled array
/** SLOW: selected/disabled as Array — include? O(S) per option element */
static long optionsForSelectSlow(int numOptions, int numSelected) {
List<String> selected = new ArrayList<>();
for (int i = 0; i < numSelected; i++) selected.add("v" + i);
long ops = 0;
// container.map per element check Array#include?
for (int i = 0; i < numOptions; i++) {
String value = "v" + (i % (numSelected * 2));
// Array(selected).include? value O(S) scan
for (String s : selected) { ops++; if (s.equals(value)) break; }
}
return ops;
}
/** FAST: selected/disabled as Set — O(1) per option */
static long optionsForSelectFast(int numOptions, int numSelected) {
Set<String> selectedSet = new HashSet<>();
for (int i = 0; i < numSelected; i++) selectedSet.add("v" + i);
long ops = 0;
for (int i = 0; i < numOptions; i++) {
String value = "v" + (i % (numSelected * 2));
ops++; // O(1) set lookup
selectedSet.contains(value);
}
return ops;
}
// rails-0013: CollectionHelpers render_collection selected set
/** SLOW: Array(current_value).map.include? rebuilt per item per option type */
static long collectionHelpersSlow(int collSize, int optionValues, int optionTypes) {
// simulate [:checked, :selected, :disabled, :readonly] × collection size
List<String> optArr = new ArrayList<>();
for (int i = 0; i < optionValues; i++) optArr.add("item_" + i);
long ops = 0;
for (int item = 0; item < collSize; item++) {
String value = "item_" + (item % (optionValues * 2));
for (int t = 0; t < optionTypes; t++) {
// Array(current_value).map(&:to_s).include?(value.to_s) per option type
for (String v : optArr) { ops++; if (v.equals(value)) break; }
}
}
return ops;
}
/** FAST: Sets pre-built before render_collection loop */
static long collectionHelpersFast(int collSize, int optionValues, int optionTypes) {
Set<String> optSet = new HashSet<>();
for (int i = 0; i < optionValues; i++) optSet.add("item_" + i);
long ops = 0;
for (int item = 0; item < collSize; item++) {
String value = "item_" + (item % (optionValues * 2));
for (int t = 0; t < optionTypes; t++) {
ops++; // O(1) set lookup
optSet.contains(value);
}
}
return ops;
}
// rails-0014: ActiveJob::Arguments symbol_keys array in transform_keys
/** SLOW: symbol_keys.include? Array scan inside transform_keys loop */
static long symbolKeysSlow(int hashKeys, int symbolKeys) {
List<String> symArr = new ArrayList<>();
for (int i = 0; i < symbolKeys; i++) symArr.add("key_" + i);
long ops = 0;
// hash.to_h.transform_keys iterate all hash keys, check each against symbol_keys
for (int k = 0; k < hashKeys; k++) {
String key = "key_" + (k % (symbolKeys * 2));
for (String s : symArr) { ops++; if (s.equals(key)) break; }
}
return ops;
}
/** FAST: convert symbol_keys to Set before the loop */
static long symbolKeysFast(int hashKeys, int symbolKeys) {
Set<String> symSet = new HashSet<>();
for (int i = 0; i < symbolKeys; i++) symSet.add("key_" + i);
long ops = 0;
for (int k = 0; k < hashKeys; k++) {
String key = "key_" + (k % (symbolKeys * 2));
ops++; // O(1) set lookup
symSet.contains(key);
}
return ops;
}
// rails-0015: schema_statements detect+count duplicate versions
/** SLOW: inserting.detect { |v| inserting.count(v) > 1 } — O(V²) */
static long duplicateVersionSlow(int versions) {
List<Integer> inserting = new ArrayList<>();
for (int i = 0; i < versions; i++) inserting.add(i * 100);
// no actual duplicate; worst case scans all
long ops = 0;
Integer dup = null;
for (Integer v : inserting) {
int c = 0;
for (Integer x : inserting) { ops++; if (x.equals(v)) c++; }
if (c > 1) { dup = v; break; }
}
return ops;
}
/** FAST: tally-based frequency map, O(V) */
static long duplicateVersionFast(int versions) {
List<Integer> inserting = new ArrayList<>();
for (int i = 0; i < versions; i++) inserting.add(i * 100);
long ops = 0;
// build frequency map in one pass
Map<Integer, Integer> freq = new HashMap<>();
for (Integer v : inserting) { ops++; freq.merge(v, 1, Integer::sum); }
// scan for duplicate once
Integer dup = null;
for (Map.Entry<Integer, Integer> e : freq.entrySet()) {
ops++;
if (e.getValue() > 1) { dup = e.getKey(); break; }
}
return ops;
}
// rails-0016: SQLite3 copy_table_indexes/copy_table_contents
/** SLOW: to_column_names.include? Array scan inside indexes.each × columns.select */
static long sqlite3CopyIndexesSlow(int numIndexes, int colsPerIndex, int tableColumns) {
List<String> toColumns = new ArrayList<>();
for (int i = 0; i < tableColumns; i++) toColumns.add("col_" + i);
long ops = 0;
for (int idx = 0; idx < numIndexes; idx++) {
// per index: to_column_names rebuilt as Array, then scanned per column
for (int c = 0; c < colsPerIndex; c++) {
String col = "col_" + (c % (tableColumns * 2));
for (String tc : toColumns) { ops++; if (tc.equals(col)) break; }
}
}
return ops;
}
/** FAST: to_column_names as Set — built once per (or before) index loop */
static long sqlite3CopyIndexesFast(int numIndexes, int colsPerIndex, int tableColumns) {
Set<String> toColSet = new HashSet<>();
for (int i = 0; i < tableColumns; i++) toColSet.add("col_" + i);
long ops = 0;
for (int idx = 0; idx < numIndexes; idx++) {
for (int c = 0; c < colsPerIndex; c++) {
String col = "col_" + (c % (tableColumns * 2));
ops++; // O(1) set lookup
toColSet.contains(col);
}
}
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;
@ -350,7 +514,7 @@ public class RailsTest {
}
public static void main(String[] args) {
System.out.println("=== UNIT rails-0001..0011: Ruby on Rails CWE-407 ===");
System.out.println("=== UNIT rails-0001..0016: Ruby on Rails CWE-407 ===");
System.out.println();
final int LOADERS=500, FUTURE=300, ROUNDS=20;
@ -394,6 +558,26 @@ public class RailsTest {
long s9=tzConversionSlow(MODELS,COLS,SKIP), f9=tzConversionFast(MODELS,COLS,SKIP);
bench("rails-0011 TimeZoneConversion skip_list", ()->tzConversionSlow(MODELS,COLS,SKIP), ()->tzConversionFast(MODELS,COLS,SKIP), s9, f9);
final int OPT_N=500, OPT_S=50;
long s10=optionsForSelectSlow(OPT_N,OPT_S), f10=optionsForSelectFast(OPT_N,OPT_S);
bench("rails-0012 options_for_select selected Array", ()->optionsForSelectSlow(OPT_N,OPT_S), ()->optionsForSelectFast(OPT_N,OPT_S), s10, f10);
final int COLL_SIZE=200, COLL_VALS=20, OPT_TYPES=4;
long s11=collectionHelpersSlow(COLL_SIZE,COLL_VALS,OPT_TYPES), f11=collectionHelpersFast(COLL_SIZE,COLL_VALS,OPT_TYPES);
bench("rails-0013 CollectionHelpers option Array rebuild", ()->collectionHelpersSlow(COLL_SIZE,COLL_VALS,OPT_TYPES), ()->collectionHelpersFast(COLL_SIZE,COLL_VALS,OPT_TYPES), s11, f11);
final int HASH_KEYS=100, SYM_KEYS=30;
long s12=symbolKeysSlow(HASH_KEYS,SYM_KEYS), f12=symbolKeysFast(HASH_KEYS,SYM_KEYS);
bench("rails-0014 ActiveJob symbol_keys Array", ()->symbolKeysSlow(HASH_KEYS,SYM_KEYS), ()->symbolKeysFast(HASH_KEYS,SYM_KEYS), s12, f12);
final int DUP_VERS=500;
long s13=duplicateVersionSlow(DUP_VERS), f13=duplicateVersionFast(DUP_VERS);
bench("rails-0015 schema_statements detect+count O(V²)", ()->duplicateVersionSlow(DUP_VERS), ()->duplicateVersionFast(DUP_VERS), s13, f13);
final int IDXS2=20, COLS_PER=10, TCOLS=50;
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);
System.out.println();
int pass = 0;
assert s0 > f0 * 10 : "rails-0001 expected >10x"; pass++;
@ -406,9 +590,14 @@ public class RailsTest {
assert s7 > f7 * 5 : "rails-0009 expected >5x"; pass++;
assert s8 > f8 * 5 : "rails-0010 expected >5x"; pass++;
assert s9 > f9 * 5 : "rails-0011 expected >5x"; pass++;
assert s10 > f10 * 5 : "rails-0012 expected >5x"; pass++;
assert s11 > f11 * 5 : "rails-0013 expected >5x"; pass++;
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 preloaderFast(10,5,2) >= 0; pass++;
System.out.printf("%d/11 PASS — rails-0001..0011: CWE-407 in preloader/callbacks/enumerable/schema/hooks/enum/filter/encryption/timezone%n", pass);
System.out.printf("Hotpaths: Preloader::Batch, skip_callback, Enumerable#excluding, SchemaDumper, lazy_load_hooks, FilterAttributeHandler, AutoFilteredParams, TimeZoneConversion%n");
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");
}
}