Each MOAD now has a synthetic defective specimen and fixed specimen proven from first principles across three test tiers: Unit (tests/unit/Moad000X*.java): - Correctness: defective and fixed produce identical functional output - Defect behavior: defective specimen exhibits the defect (measurable) - Fix behavior: fixed specimen eliminates the defect Integration (tests/integration/AllMoadsIntegrationTest.java): - All 9 MOADs proven at medium scale (N=500-2000) - MOAD-0001: O(N^2) vs O(N) list scan at N=1000 - MOAD-0002: 500 sessions trample each other (defective) vs coexist (fixed) - MOAD-0003: 250 anonymous requests leak auth identity (defective) vs zero (fixed) - MOAD-0004: 3000 credential exposures across 1000 requests (defective) vs zero (fixed) - MOAD-0005: 500 computes for 500 concurrent misses vs exactly 1 - MOAD-0006: all 500 passwords extractable from DB (defective) vs unextractable (fixed) - MOAD-0007: N=2000 spatial objects, defective visits all 2000 vs O(log N + k) - MOAD-0009: 990 wasted firings for 1000 ticks / 10 events vs zero waste - MOAD-0011: 10240 NFA steps vs 13 steps on N=12 adversarial input (788x) Functional (tests/functional/AllMoadsFunctionalTest.java): - MOAD-0005: real-thread contention proves herd (defective >1 compute, fixed exactly 1) - MOAD-0007: N=50000 spatial objects, 50M defective probes vs 516K fixed (97x speedup) - MOAD-0009: 10000 ticks / 10 events, 9990 wasted firings vs zero (1000x ratio) - MOAD-0011: N=16 adversarial, 163840 defective steps vs 17 fixed (9638x ratio) Support algorithms (tests/support/Moad000X*.java): - Moad0002Algorithm: shared mutable global state (DefectiveAudioSystem / FixedAudioSystem + Context) - Moad0003Algorithm: ThreadLocal not cleared (handleDefective / handleFixed with finally) - Moad0004Algorithm: HTTP headers logged verbatim (logDefective / logFixed with CREDENTIAL_HEADERS denylist) - Moad0005Algorithm: get+null+compute+put (DefectiveCache HashMap / FixedCache ConcurrentHashMap.computeIfAbsent) - Moad0006Algorithm: Base64 password storage (DefectiveCredentialStore / FixedCredentialStore SHA-256+salt) - Moad0007Algorithm: linear spatial scan (queryDefective list / queryFixed sorted array + binary search) - Moad0009Algorithm: timer-driven polling (runDefectiveScheduler / runFixedEventDriven) - Moad0011Algorithm: PCRE nested quantifiers (matchDefective backtracking NFA / matchFixed linear NFA) Makefile: added unit-moad-0002 through unit-moad-0011 targets, integration-all-moads, functional-all-moads. integration and functional targets now depend on all-MOADs variants.
142 lines
6.4 KiB
Java
142 lines
6.4 KiB
Java
package unit;
|
|
|
|
import support.Moad0003Algorithm;
|
|
|
|
/**
|
|
* Unit tests for MOAD-0003: A Leaked Context.
|
|
*
|
|
* Proves from first principles:
|
|
* 1. Defective: ThreadLocal set in request R1 remains accessible after R1
|
|
* completes — the next request R2 (which never calls set()) inherits R1's
|
|
* identity. This is the thread-pool leak: the thread is reused, but the
|
|
* ThreadLocal is not cleared between units of work.
|
|
* 2. Fixed: ThreadLocal.remove() in a finally block ensures R2 sees null —
|
|
* each request starts from a clean slate.
|
|
* 3. Both correctly return the identity when explicitly set.
|
|
* 4. Partial-set scenario: R2 sets its own identity and removes it correctly
|
|
* even when R1 failed to clean up in the defective variant.
|
|
*
|
|
* No build tool required. Compile and run:
|
|
*
|
|
* cd tests
|
|
* java -m jdk.compiler/com.sun.tools.javac.Main -cp . \
|
|
* support/Moad0003Algorithm.java unit/Moad0003UnitTest.java
|
|
* java -cp . unit.Moad0003UnitTest
|
|
*/
|
|
public class Moad0003UnitTest {
|
|
|
|
private static int passed = 0;
|
|
private static int failed = 0;
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("=== Moad0003UnitTest (A Leaked Context) ===\n");
|
|
|
|
System.out.println("-- Correctness: identity returned correctly when set --");
|
|
testDefectiveReturnsSetIdentity();
|
|
testFixedReturnsSetIdentity();
|
|
|
|
System.out.println("\n-- Defect: stale identity leaks to anonymous next request --");
|
|
testDefectiveLeaksToAnonymousRequest();
|
|
testDefectiveLeaksAcrossMultipleRequests();
|
|
|
|
System.out.println("\n-- Fix: ThreadLocal.remove() cleans up before next request --");
|
|
testFixedAnonymousRequestSeesNull();
|
|
testFixedMultipleRequestsClean();
|
|
|
|
System.out.printf("\n%d passed, %d failed%n", passed, failed);
|
|
if (failed > 0) System.exit(1);
|
|
}
|
|
|
|
// ── Correctness ───────────────────────────────────────────────────────────
|
|
|
|
static void testDefectiveReturnsSetIdentity() {
|
|
Moad0003Algorithm.reset();
|
|
String result = Moad0003Algorithm.handleDefective("alice");
|
|
assertEqual("defective: returns set identity 'alice'", "alice", result);
|
|
Moad0003Algorithm.reset(); // clean up after defective handler
|
|
}
|
|
|
|
static void testFixedReturnsSetIdentity() {
|
|
Moad0003Algorithm.reset();
|
|
String result = Moad0003Algorithm.handleFixed("bob");
|
|
assertEqual("fixed: returns set identity 'bob'", "bob", result);
|
|
// handleFixed removes ThreadLocal automatically
|
|
}
|
|
|
|
// ── Defect ────────────────────────────────────────────────────────────────
|
|
|
|
static void testDefectiveLeaksToAnonymousRequest() {
|
|
Moad0003Algorithm.reset();
|
|
|
|
// Request R1: authenticated as "alice"
|
|
Moad0003Algorithm.handleDefective("alice");
|
|
// R1 completes but DOES NOT call remove() — ThreadLocal still holds "alice"
|
|
|
|
// Request R2: anonymous — never calls set()
|
|
// Simulates the same pooled thread handling the next request.
|
|
String leaked = Moad0003Algorithm.handleDefectiveAnonymous();
|
|
|
|
// DEFECT: R2 sees "alice" even though it never set any identity.
|
|
// A CSRF-style confusion: R2 could perform privileged operations as "alice".
|
|
assertEqual("defective: anonymous request leaks alice's identity", "alice", leaked);
|
|
|
|
Moad0003Algorithm.reset();
|
|
}
|
|
|
|
static void testDefectiveLeaksAcrossMultipleRequests() {
|
|
Moad0003Algorithm.reset();
|
|
|
|
// R1 sets identity, completes without cleanup
|
|
Moad0003Algorithm.handleDefective("carol");
|
|
// R2 anonymous — sees leaked "carol"
|
|
String leak1 = Moad0003Algorithm.handleDefectiveAnonymous();
|
|
// R3 anonymous — still sees "carol" (ThreadLocal persists until explicitly removed)
|
|
String leak2 = Moad0003Algorithm.handleDefectiveAnonymous();
|
|
|
|
assertEqual("defective: first anonymous request leaks 'carol'", "carol", leak1);
|
|
assertEqual("defective: second anonymous request still leaks 'carol'", "carol", leak2);
|
|
|
|
Moad0003Algorithm.reset();
|
|
}
|
|
|
|
// ── Fix ───────────────────────────────────────────────────────────────────
|
|
|
|
static void testFixedAnonymousRequestSeesNull() {
|
|
Moad0003Algorithm.reset();
|
|
|
|
// R1: authenticated — handleFixed removes ThreadLocal in finally
|
|
Moad0003Algorithm.handleFixed("alice");
|
|
// ThreadLocal is now null — handleFixed called remove() before returning
|
|
|
|
// R2: anonymous — sees null (clean slate)
|
|
String identity = Moad0003Algorithm.handleFixedAnonymous();
|
|
assertEqual("fixed: anonymous request after fixed handler sees null", null, identity);
|
|
}
|
|
|
|
static void testFixedMultipleRequestsClean() {
|
|
Moad0003Algorithm.reset();
|
|
|
|
// Simulate 3 requests on the same pooled thread, alternating auth/anon
|
|
String r1 = Moad0003Algorithm.handleFixed("alice"); // auth
|
|
String r2 = Moad0003Algorithm.handleFixedAnonymous(); // anon — must be null
|
|
String r3 = Moad0003Algorithm.handleFixed("bob"); // auth
|
|
String r4 = Moad0003Algorithm.handleFixedAnonymous(); // anon — must be null
|
|
|
|
assertEqual("fixed: R1 returns 'alice'", "alice", r1);
|
|
assertEqual("fixed: R2 anonymous sees null (not alice)", null, r2);
|
|
assertEqual("fixed: R3 returns 'bob'", "bob", r3);
|
|
assertEqual("fixed: R4 anonymous sees null (not bob)", null, r4);
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
|
|
static void assertEqual(String label, String expected, String actual) {
|
|
if (expected == null ? actual == null : expected.equals(actual)) {
|
|
System.out.printf(" PASS: %s%n", label);
|
|
passed++;
|
|
} else {
|
|
System.out.printf(" FAIL: %s — expected '%s', got '%s'%n", label, expected, actual);
|
|
failed++;
|
|
}
|
|
}
|
|
}
|