270 lines
10 KiB
Java
270 lines
10 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* CWE-407 unit tests for Quarkus.
|
|
*
|
|
* quarkus-0001: BeanInfo.getBoundInterceptors()
|
|
* independent-projects/arc/processor/.../BeanInfo.java
|
|
* Builds deduplicated interceptor list using ArrayList.contains() inside
|
|
* nested loops over lifecycle+method interceptions.
|
|
* Complexity: O(M * I^2) where M = intercepted methods, I = interceptors.
|
|
* Fix: LinkedHashSet for O(1) dedup → O(M * I).
|
|
*
|
|
* quarkus-0002: BeanInfo.getBoundDecorators()
|
|
* Same file. ArrayList.contains(dm.decorator) inside loop over decorated methods.
|
|
* Complexity: O(M * D^2) where M = decorated methods, D = decorators.
|
|
* Fix: LinkedHashSet for O(1) dedup → O(M * D).
|
|
*
|
|
* quarkus-0003: RuntimeUpdatesProcessor.checkForClassFilesChangesInModule()
|
|
* core/deployment/.../dev/RuntimeUpdatesProcessor.java
|
|
* moduleChangedSourceFiles is ArrayList<Path>; contains(sourceFilePath) called
|
|
* for every class file in the module during dev mode hot reload.
|
|
* Complexity: O(C * S) where C = class files, S = changed source files.
|
|
* Fix: change declaration to Set<Path> (LinkedHashSet) → O(C).
|
|
*
|
|
* quarkus-0004: CORSFilter.handle()
|
|
* extensions/vertx-http/.../cors/CORSFilter.java
|
|
* corsConfig.origins().get().contains(origin) on List<String> executed per
|
|
* HTTP request. O(O) per request where O = configured origins count.
|
|
* Fix: pre-build HashSet<String> of exact origins at construction → O(1) per request.
|
|
*/
|
|
public class QuarkusTest {
|
|
|
|
// -----------------------------------------------------------------------
|
|
// quarkus-0001 / quarkus-0002: getBoundInterceptors / getBoundDecorators
|
|
// -----------------------------------------------------------------------
|
|
|
|
/** Defect: deduplicate using ArrayList.contains — O(N^2). */
|
|
static List<Integer> deduplicateList(List<Integer> inputs) {
|
|
List<Integer> bound = new ArrayList<>();
|
|
for (int item : inputs) {
|
|
if (!bound.contains(item)) { // O(N) per call — defect
|
|
bound.add(item);
|
|
}
|
|
}
|
|
return bound;
|
|
}
|
|
|
|
/** Fix: deduplicate using LinkedHashSet — O(N) total, preserves order. */
|
|
static List<Integer> deduplicateSet(List<Integer> inputs) {
|
|
Set<Integer> seen = new LinkedHashSet<>();
|
|
for (int item : inputs) {
|
|
seen.add(item); // O(1) per call — fix
|
|
}
|
|
return new ArrayList<>(seen);
|
|
}
|
|
|
|
static void testQuarkus0001_getBoundInterceptors() throws Exception {
|
|
// Simulate M=200 intercepted methods, each with I=100 interceptors (some repeated)
|
|
int M = 200;
|
|
int I = 100;
|
|
int totalInterceptors = 50; // universe of unique interceptors
|
|
|
|
List<Integer> allInterceptors = new ArrayList<>();
|
|
Random rng = new Random(42);
|
|
for (int m = 0; m < M; m++) {
|
|
for (int i = 0; i < I; i++) {
|
|
allInterceptors.add(rng.nextInt(totalInterceptors));
|
|
}
|
|
}
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int trial = 0; trial < 1000; trial++) {
|
|
deduplicateList(allInterceptors);
|
|
}
|
|
long defectNs = System.nanoTime() - t0;
|
|
|
|
long t1 = System.nanoTime();
|
|
for (int trial = 0; trial < 1000; trial++) {
|
|
deduplicateSet(allInterceptors);
|
|
}
|
|
long fixNs = System.nanoTime() - t1;
|
|
|
|
double ratio = (double) defectNs / fixNs;
|
|
System.out.printf("quarkus-0001 getBoundInterceptors: defect=%.1fms fix=%.1fms ratio=%.1fx%n",
|
|
defectNs / 1e6, fixNs / 1e6, ratio);
|
|
|
|
if (ratio < 2.0) {
|
|
throw new AssertionError("Expected ratio >= 2.0, got " + ratio);
|
|
}
|
|
System.out.println("quarkus-0001 PASS");
|
|
}
|
|
|
|
static void testQuarkus0002_getBoundDecorators() throws Exception {
|
|
// Simulate M=150 decorated methods, each with D=80 decorators (some repeated)
|
|
int M = 150;
|
|
int D = 80;
|
|
int totalDecorators = 30;
|
|
|
|
List<Integer> allDecorators = new ArrayList<>();
|
|
Random rng = new Random(123);
|
|
for (int m = 0; m < M; m++) {
|
|
for (int d = 0; d < D; d++) {
|
|
allDecorators.add(rng.nextInt(totalDecorators));
|
|
}
|
|
}
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int trial = 0; trial < 1000; trial++) {
|
|
deduplicateList(allDecorators);
|
|
}
|
|
long defectNs = System.nanoTime() - t0;
|
|
|
|
long t1 = System.nanoTime();
|
|
for (int trial = 0; trial < 1000; trial++) {
|
|
deduplicateSet(allDecorators);
|
|
}
|
|
long fixNs = System.nanoTime() - t1;
|
|
|
|
double ratio = (double) defectNs / fixNs;
|
|
System.out.printf("quarkus-0002 getBoundDecorators: defect=%.1fms fix=%.1fms ratio=%.1fx%n",
|
|
defectNs / 1e6, fixNs / 1e6, ratio);
|
|
|
|
if (ratio < 2.0) {
|
|
throw new AssertionError("Expected ratio >= 2.0, got " + ratio);
|
|
}
|
|
System.out.println("quarkus-0002 PASS");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// quarkus-0003: checkForClassFilesChangesInModule — dev mode hot reload
|
|
// -----------------------------------------------------------------------
|
|
|
|
/** Defect: List<String>.contains per class file — O(C * S). */
|
|
static int scanModuleDefect(List<String> classFiles, List<String> changedSourceFiles) {
|
|
int deletions = 0;
|
|
for (String classFile : classFiles) {
|
|
String sourceFile = classFile.replace(".class", ".java");
|
|
if (changedSourceFiles.contains(sourceFile)) { // O(S) per call — defect
|
|
deletions++;
|
|
}
|
|
}
|
|
return deletions;
|
|
}
|
|
|
|
/** Fix: Set<String>.contains per class file — O(C). */
|
|
static int scanModuleFix(List<String> classFiles, Set<String> changedSourceFiles) {
|
|
int deletions = 0;
|
|
for (String classFile : classFiles) {
|
|
String sourceFile = classFile.replace(".class", ".java");
|
|
if (changedSourceFiles.contains(sourceFile)) { // O(1) per call — fix
|
|
deletions++;
|
|
}
|
|
}
|
|
return deletions;
|
|
}
|
|
|
|
static void testQuarkus0003_devModeChangedSourceFiles() throws Exception {
|
|
// Simulate: C=2000 class files, S=200 changed source files
|
|
int C = 2000;
|
|
int S = 200;
|
|
|
|
List<String> classFiles = new ArrayList<>(C);
|
|
List<String> changedSourceList = new ArrayList<>(S);
|
|
Set<String> changedSourceSet = new LinkedHashSet<>(S * 2);
|
|
|
|
for (int i = 0; i < C; i++) {
|
|
classFiles.add("com/example/Foo" + i + ".class");
|
|
}
|
|
for (int i = 0; i < S; i++) {
|
|
String src = "com/example/Foo" + (i * 5) + ".java";
|
|
changedSourceList.add(src);
|
|
changedSourceSet.add(src);
|
|
}
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int trial = 0; trial < 200; trial++) {
|
|
scanModuleDefect(classFiles, changedSourceList);
|
|
}
|
|
long defectNs = System.nanoTime() - t0;
|
|
|
|
long t1 = System.nanoTime();
|
|
for (int trial = 0; trial < 200; trial++) {
|
|
scanModuleFix(classFiles, changedSourceSet);
|
|
}
|
|
long fixNs = System.nanoTime() - t1;
|
|
|
|
double ratio = (double) defectNs / fixNs;
|
|
System.out.printf("quarkus-0003 devModeChangedSourceFiles: defect=%.1fms fix=%.1fms ratio=%.1fx%n",
|
|
defectNs / 1e6, fixNs / 1e6, ratio);
|
|
|
|
if (ratio < 5.0) {
|
|
throw new AssertionError("Expected ratio >= 5.0, got " + ratio);
|
|
}
|
|
System.out.println("quarkus-0003 PASS");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// quarkus-0004: CORSFilter.handle — origins list lookup per request
|
|
// -----------------------------------------------------------------------
|
|
|
|
/** Defect: List<String>.contains per HTTP request — O(O). */
|
|
static int handleRequestsDefect(List<String> origins, String[] incomingOrigins) {
|
|
int allowed = 0;
|
|
for (String origin : incomingOrigins) {
|
|
if (origins.contains(origin)) { // O(O) per request — defect
|
|
allowed++;
|
|
}
|
|
}
|
|
return allowed;
|
|
}
|
|
|
|
/** Fix: pre-built HashSet<String>.contains per request — O(1). */
|
|
static int handleRequestsFix(Set<String> originsSet, String[] incomingOrigins) {
|
|
int allowed = 0;
|
|
for (String origin : incomingOrigins) {
|
|
if (originsSet.contains(origin)) { // O(1) per request — fix
|
|
allowed++;
|
|
}
|
|
}
|
|
return allowed;
|
|
}
|
|
|
|
static void testQuarkus0004_corsOriginsLookup() throws Exception {
|
|
// Simulate: O=100 configured origins, R=100000 requests
|
|
int O = 100;
|
|
int R = 100000;
|
|
|
|
List<String> originsList = new ArrayList<>(O);
|
|
Set<String> originsSet = new HashSet<>(O * 2);
|
|
for (int i = 0; i < O; i++) {
|
|
originsList.add("https://example" + i + ".com");
|
|
originsSet.add("https://example" + i + ".com");
|
|
}
|
|
|
|
// Requests target last origin (worst case for list scan)
|
|
String[] requests = new String[R];
|
|
for (int i = 0; i < R; i++) {
|
|
requests[i] = "https://example" + ((i % O)) + ".com";
|
|
}
|
|
|
|
long t0 = System.nanoTime();
|
|
handleRequestsDefect(originsList, requests);
|
|
long defectNs = System.nanoTime() - t0;
|
|
|
|
long t1 = System.nanoTime();
|
|
handleRequestsFix(originsSet, requests);
|
|
long fixNs = System.nanoTime() - t1;
|
|
|
|
double ratio = (double) defectNs / fixNs;
|
|
System.out.printf("quarkus-0004 corsOriginsLookup: defect=%.1fms fix=%.1fms ratio=%.1fx%n",
|
|
defectNs / 1e6, fixNs / 1e6, ratio);
|
|
|
|
if (ratio < 2.0) {
|
|
throw new AssertionError("Expected ratio >= 2.0, got " + ratio);
|
|
}
|
|
System.out.println("quarkus-0004 PASS");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Main
|
|
// -----------------------------------------------------------------------
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
testQuarkus0001_getBoundInterceptors();
|
|
testQuarkus0002_getBoundDecorators();
|
|
testQuarkus0003_devModeChangedSourceFiles();
|
|
testQuarkus0004_corsOriginsLookup();
|
|
System.out.println("ALL PASS");
|
|
}
|
|
}
|