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 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 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 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 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 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 endpoints = new ArrayList<>(); Map 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"); } }