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:
parent
eb9612e4bf
commit
3735145aa5
47 changed files with 3488 additions and 33 deletions
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue