204 lines
9.1 KiB
Java
204 lines
9.1 KiB
Java
import java.util.*;
|
||
|
||
/**
|
||
* CWE-407 unit tests for CockroachDB — three defects:
|
||
*
|
||
* cockroach-0001: IndexesUsed.add() calls slices.Contains on a growing slice
|
||
* for each of N add() calls → O(N²) index deduplication during
|
||
* SQL query plan building.
|
||
*
|
||
* cockroach-0002: BuildFingerprintQueryForIndex / BuildExperimentalFingerprintQueryForIndex
|
||
* call slices.Contains(ignoredColumns, col) for every column in
|
||
* every index column loop → O(C × I) where C = columns, I = ignored list.
|
||
*
|
||
* cockroach-0003: EnsureUserOnlyBelongsToRoles iterates currentRoles (size R) and
|
||
* calls slices.Contains(roles, role) (size D) for each → O(R × D)
|
||
* during LDAP-driven role synchronisation.
|
||
*/
|
||
public class CockroachTest {
|
||
|
||
// ── cockroach-0001 ────────────────────────────────────────────────────────
|
||
|
||
/** Defective: ArrayList.contains inside an accumulation loop → O(N²). */
|
||
static List<long[]> indexesUsedAdd_defective(int n) {
|
||
List<long[]> indexes = new ArrayList<>();
|
||
for (long i = 0; i < n; i++) {
|
||
long tableID = i % 50;
|
||
long indexID = i % 20;
|
||
long[] entry = new long[]{tableID, indexID};
|
||
boolean found = false;
|
||
for (long[] e : indexes) {
|
||
if (e[0] == entry[0] && e[1] == entry[1]) { found = true; break; }
|
||
}
|
||
if (!found) indexes.add(entry);
|
||
}
|
||
return indexes;
|
||
}
|
||
|
||
/** Fixed: HashMap set for O(1) membership → O(N) total. */
|
||
static List<long[]> indexesUsedAdd_fixed(int n) {
|
||
List<long[]> indexes = new ArrayList<>();
|
||
Set<Long> seen = new HashSet<>();
|
||
for (long i = 0; i < n; i++) {
|
||
long tableID = i % 50;
|
||
long indexID = i % 20;
|
||
long key = tableID * 1_000_000L + indexID;
|
||
if (seen.add(key)) {
|
||
indexes.add(new long[]{tableID, indexID});
|
||
}
|
||
}
|
||
return indexes;
|
||
}
|
||
|
||
// ── cockroach-0002 ────────────────────────────────────────────────────────
|
||
|
||
/** Defective: linear scan of ignoredColumns for every column → O(C × I). */
|
||
static List<String> fingerprintColumns_defective(List<String> columns, List<String> ignored) {
|
||
List<String> result = new ArrayList<>();
|
||
for (String col : columns) {
|
||
if (ignored.contains(col)) continue; // O(I) per column
|
||
result.add(col);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/** Fixed: build a HashSet once, then O(1) per column → O(C + I). */
|
||
static List<String> fingerprintColumns_fixed(List<String> columns, List<String> ignored) {
|
||
Set<String> ignoredSet = new HashSet<>(ignored);
|
||
List<String> result = new ArrayList<>();
|
||
for (String col : columns) {
|
||
if (!ignoredSet.contains(col)) result.add(col);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// ── cockroach-0003 ────────────────────────────────────────────────────────
|
||
|
||
/** Defective: for each currentRole call roles.contains → O(R × D). */
|
||
static List<String> rolesToRevoke_defective(Set<String> currentRoles, List<String> desiredRoles) {
|
||
List<String> toRevoke = new ArrayList<>();
|
||
for (String role : currentRoles) {
|
||
if (!desiredRoles.contains(role)) toRevoke.add(role); // O(D) per role
|
||
}
|
||
return toRevoke;
|
||
}
|
||
|
||
/** Fixed: build desiredSet once → O(R + D). */
|
||
static List<String> rolesToRevoke_fixed(Set<String> currentRoles, List<String> desiredRoles) {
|
||
Set<String> desiredSet = new HashSet<>(desiredRoles);
|
||
List<String> toRevoke = new ArrayList<>();
|
||
for (String role : currentRoles) {
|
||
if (!desiredSet.contains(role)) toRevoke.add(role);
|
||
}
|
||
return toRevoke;
|
||
}
|
||
|
||
// ── helpers ───────────────────────────────────────────────────────────────
|
||
|
||
static long bench(Runnable r) {
|
||
long t0 = System.nanoTime();
|
||
r.run();
|
||
return System.nanoTime() - t0;
|
||
}
|
||
|
||
// ── main ─────────────────────────────────────────────────────────────────
|
||
|
||
public static void main(String[] args) {
|
||
int pass = 0, fail = 0;
|
||
|
||
// --- cockroach-0001 correctness ---
|
||
{
|
||
List<long[]> def = indexesUsedAdd_defective(500);
|
||
List<long[]> fix = indexesUsedAdd_fixed(500);
|
||
if (def.size() == fix.size()) {
|
||
System.out.println("PASS cockroach-0001 correctness (size=" + def.size() + ")");
|
||
pass++;
|
||
} else {
|
||
System.out.println("FAIL cockroach-0001 correctness def=" + def.size() + " fix=" + fix.size());
|
||
fail++;
|
||
}
|
||
}
|
||
|
||
// --- cockroach-0001 performance ---
|
||
{
|
||
int N = 2000;
|
||
long tDef = bench(() -> indexesUsedAdd_defective(N));
|
||
long tFix = bench(() -> indexesUsedAdd_fixed(N));
|
||
double ratio = (double) tDef / Math.max(tFix, 1);
|
||
System.out.printf("PASS cockroach-0001 perf defective=%dms fixed=%dms ratio=%.1fx%n",
|
||
tDef / 1_000_000, tFix / 1_000_000, ratio);
|
||
if (ratio >= 2.0) pass++; else { System.out.println("FAIL cockroach-0001 perf ratio too low"); fail++; }
|
||
}
|
||
|
||
// --- cockroach-0002 correctness ---
|
||
{
|
||
List<String> columns = new ArrayList<>();
|
||
for (int i = 0; i < 200; i++) columns.add("col_" + i);
|
||
List<String> ignored = new ArrayList<>();
|
||
for (int i = 0; i < 50; i++) ignored.add("col_" + (i * 4));
|
||
|
||
List<String> def = fingerprintColumns_defective(columns, ignored);
|
||
List<String> fix = fingerprintColumns_fixed(columns, ignored);
|
||
if (def.equals(fix)) {
|
||
System.out.println("PASS cockroach-0002 correctness (kept=" + def.size() + ")");
|
||
pass++;
|
||
} else {
|
||
System.out.println("FAIL cockroach-0002 correctness");
|
||
fail++;
|
||
}
|
||
}
|
||
|
||
// --- cockroach-0002 performance ---
|
||
{
|
||
List<String> columns = new ArrayList<>();
|
||
for (int i = 0; i < 1000; i++) columns.add("col_" + i);
|
||
List<String> ignored = new ArrayList<>();
|
||
for (int i = 0; i < 500; i++) ignored.add("col_" + (i * 2));
|
||
|
||
long tDef = bench(() -> fingerprintColumns_defective(columns, ignored));
|
||
long tFix = bench(() -> fingerprintColumns_fixed(columns, ignored));
|
||
double ratio = (double) tDef / Math.max(tFix, 1);
|
||
System.out.printf("PASS cockroach-0002 perf defective=%dms fixed=%dms ratio=%.1fx%n",
|
||
tDef / 1_000_000, tFix / 1_000_000, ratio);
|
||
if (ratio >= 1.5) pass++; else { System.out.println("FAIL cockroach-0002 perf ratio too low"); fail++; }
|
||
}
|
||
|
||
// --- cockroach-0003 correctness ---
|
||
{
|
||
Set<String> current = new HashSet<>();
|
||
for (int i = 0; i < 100; i++) current.add("role_" + i);
|
||
List<String> desired = new ArrayList<>();
|
||
for (int i = 0; i < 60; i++) desired.add("role_" + i);
|
||
|
||
List<String> def = rolesToRevoke_defective(current, desired);
|
||
List<String> fix = rolesToRevoke_fixed(current, desired);
|
||
Collections.sort(def); Collections.sort(fix);
|
||
if (def.equals(fix)) {
|
||
System.out.println("PASS cockroach-0003 correctness (toRevoke=" + def.size() + ")");
|
||
pass++;
|
||
} else {
|
||
System.out.println("FAIL cockroach-0003 correctness def=" + def + " fix=" + fix);
|
||
fail++;
|
||
}
|
||
}
|
||
|
||
// --- cockroach-0003 performance ---
|
||
{
|
||
Set<String> current = new HashSet<>();
|
||
for (int i = 0; i < 2000; i++) current.add("role_" + i);
|
||
List<String> desired = new ArrayList<>();
|
||
for (int i = 0; i < 1000; i++) desired.add("role_" + i);
|
||
|
||
long tDef = bench(() -> rolesToRevoke_defective(current, desired));
|
||
long tFix = bench(() -> rolesToRevoke_fixed(current, desired));
|
||
double ratio = (double) tDef / Math.max(tFix, 1);
|
||
System.out.printf("PASS cockroach-0003 perf defective=%dms fixed=%dms ratio=%.1fx%n",
|
||
tDef / 1_000_000, tFix / 1_000_000, ratio);
|
||
if (ratio >= 2.0) pass++; else { System.out.println("FAIL cockroach-0003 perf ratio too low"); fail++; }
|
||
}
|
||
|
||
System.out.println();
|
||
System.out.println("Results: " + pass + " PASS, " + fail + " FAIL");
|
||
if (fail > 0) System.exit(1);
|
||
}
|
||
}
|