java-topology/defects/grape/unit/GrapeAlgorithm.java

175 lines
7.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.*;
/**
* 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");
}
}