175 lines
7.4 KiB
Java
175 lines
7.4 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* GrapeAlgorithm — grape-0001..0003
|
||
*
|
||
* Proves CWE-407 in Grape (Ruby API framework):
|
||
* grape-0001: ValuesValidator — values Array#include? in param_array.all? O(P×V)
|
||
* grape-0002: ExceptValuesValidator — excepts Array#include? in param_array.any? O(P×E)
|
||
* grape-0003: DSL::Routing#route — endpoints Array#any? duplicate check O(N²)
|
||
*
|
||
* Run: javac -d . GrapeAlgorithm.java && java -ea unit.GrapeAlgorithm
|
||
*/
|
||
public class GrapeAlgorithm {
|
||
|
||
// ── grape-0001: ValuesValidator values.include? ───────────────────────────
|
||
|
||
/** SLOW: values is Array — Array#include? per param O(P×V) */
|
||
static long valuesValidatorSlow(int paramCount, int valuesCount) {
|
||
// Build allowlist array (e.g., permitted tag values)
|
||
List<String> valuesArr = new ArrayList<>();
|
||
for (int i = 0; i < valuesCount; i++) valuesArr.add("value_" + i);
|
||
|
||
long ops = 0;
|
||
// Simulate P submitted params, each validated against V-element array
|
||
// param_array.all? { |param| values.include?(param) }
|
||
for (int p = 0; p < paramCount; p++) {
|
||
String param = "value_" + (p % valuesCount);
|
||
for (String v : valuesArr) {
|
||
ops++;
|
||
if (v.equals(param)) break; // include? short-circuits but worst case scans all
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/** FAST: values is Set — Set#include? per param O(P+V) */
|
||
static long valuesValidatorFast(int paramCount, int valuesCount) {
|
||
// values_set = values.to_set
|
||
Set<String> valuesSet = new HashSet<>();
|
||
for (int i = 0; i < valuesCount; i++) valuesSet.add("value_" + i);
|
||
|
||
long ops = 0;
|
||
for (int p = 0; p < paramCount; p++) {
|
||
String param = "value_" + (p % valuesCount);
|
||
ops++; // O(1) Set#include?
|
||
valuesSet.contains(param);
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
// ── grape-0002: ExceptValuesValidator excepts.include? ───────────────────
|
||
|
||
/** SLOW: excepts is Array — Array#include? per param O(P×E) */
|
||
static long exceptValuesValidatorSlow(int paramCount, int exceptsCount) {
|
||
// Build blocklist array (e.g., reserved words that cannot be submitted)
|
||
List<String> exceptsArr = new ArrayList<>();
|
||
for (int i = 0; i < exceptsCount; i++) exceptsArr.add("except_" + i);
|
||
|
||
long ops = 0;
|
||
// Simulate P submitted params; raise if any is in blocklist
|
||
// param_array.any? { |param| excepts.include?(param) }
|
||
for (int p = 0; p < paramCount; p++) {
|
||
String param = "safe_" + p; // none match, forces full scan each time
|
||
for (String e : exceptsArr) {
|
||
ops++;
|
||
if (e.equals(param)) break;
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/** FAST: excepts is Set — Set#include? per param O(P+E) */
|
||
static long exceptValuesValidatorFast(int paramCount, int exceptsCount) {
|
||
Set<String> exceptsSet = new HashSet<>();
|
||
for (int i = 0; i < exceptsCount; i++) exceptsSet.add("except_" + i);
|
||
|
||
long ops = 0;
|
||
for (int p = 0; p < paramCount; p++) {
|
||
String param = "safe_" + p;
|
||
ops++; // O(1) Set#include?
|
||
exceptsSet.contains(param);
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
// ── grape-0003: DSL::Routing endpoints duplicate check ───────────────────
|
||
|
||
/**
|
||
* SLOW: endpoints is Array — endpoints.any? { |e| e.equals?(new_endpoint) }
|
||
* O(N²) total across N route registrations
|
||
*/
|
||
static long routingEndpointsSlow(int routeCount) {
|
||
List<String> endpoints = new ArrayList<>();
|
||
long ops = 0;
|
||
for (int i = 0; i < routeCount; i++) {
|
||
String newEndpoint = "GET:/path/" + i;
|
||
// endpoints.any? { |e| e.equals?(new_endpoint) }
|
||
boolean found = false;
|
||
for (String e : endpoints) {
|
||
ops++;
|
||
if (e.equals(newEndpoint)) { found = true; break; }
|
||
}
|
||
if (!found) endpoints.add(newEndpoint);
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/**
|
||
* FAST: endpoints seen in Hash — Hash#key? per registration O(N)
|
||
*/
|
||
static long routingEndpointsFast(int routeCount) {
|
||
List<String> endpoints = new ArrayList<>();
|
||
Map<String, Boolean> endpointsSeen = new HashMap<>();
|
||
long ops = 0;
|
||
for (int i = 0; i < routeCount; i++) {
|
||
String newEndpoint = "GET:/path/" + i;
|
||
ops++; // O(1) Hash#key?
|
||
if (!endpointsSeen.containsKey(newEndpoint)) {
|
||
endpointsSeen.put(newEndpoint, true);
|
||
endpoints.add(newEndpoint);
|
||
}
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
// ── bench harness ─────────────────────────────────────────────────────────
|
||
|
||
interface Bench { long run(); }
|
||
|
||
static void bench(String label, Bench slow, Bench fast, long sOps, long fOps) {
|
||
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 grape-0001..0003: Grape CWE-407 ===");
|
||
System.out.println();
|
||
|
||
final int PARAMS = 100, VALUES = 300; // grape-0001: 100 submitted, 300 allowed
|
||
final int PARAMS2 = 80, EXCEPTS = 200; // grape-0002: 80 submitted, 200 blocklisted
|
||
final int ROUTES = 600; // grape-0003: 600 route registrations
|
||
|
||
long s0 = valuesValidatorSlow(PARAMS, VALUES);
|
||
long f0 = valuesValidatorFast(PARAMS, VALUES);
|
||
bench("grape-0001 ValuesValidator values.include?",
|
||
() -> valuesValidatorSlow(PARAMS, VALUES),
|
||
() -> valuesValidatorFast(PARAMS, VALUES), s0, f0);
|
||
|
||
long s1 = exceptValuesValidatorSlow(PARAMS2, EXCEPTS);
|
||
long f1 = exceptValuesValidatorFast(PARAMS2, EXCEPTS);
|
||
bench("grape-0002 ExceptValuesValidator excepts.include?",
|
||
() -> exceptValuesValidatorSlow(PARAMS2, EXCEPTS),
|
||
() -> exceptValuesValidatorFast(PARAMS2, EXCEPTS), s1, f1);
|
||
|
||
long s2 = routingEndpointsSlow(ROUTES);
|
||
long f2 = routingEndpointsFast(ROUTES);
|
||
bench("grape-0003 DSL::Routing endpoints.any? dup check",
|
||
() -> routingEndpointsSlow(ROUTES),
|
||
() -> routingEndpointsFast(ROUTES), s2, f2);
|
||
|
||
System.out.println();
|
||
int pass = 0;
|
||
assert s0 > f0 * 5 : "grape-0001 expected >5x speedup; got slow=" + s0 + " fast=" + f0; pass++;
|
||
assert s1 > f1 * 5 : "grape-0002 expected >5x speedup; got slow=" + s1 + " fast=" + f1; pass++;
|
||
assert s2 > f2 * 5 : "grape-0003 expected >5x speedup; got slow=" + s2 + " fast=" + f2; pass++;
|
||
|
||
System.out.printf("%d/3 PASS — grape-0001..0003: CWE-407 in ValuesValidator/ExceptValuesValidator/DSL::Routing%n", pass);
|
||
System.out.printf("Hotpaths: per-request param validation (grape-0001/0002), app boot route registration (grape-0003)%n");
|
||
}
|
||
}
|