openssl-0004 + uwsgi-0001: 2 new defects (500x/249x); all 10 missing whitepaper entries restored; count 590→592
This commit is contained in:
parent
2e4f7807d5
commit
34e8d9212f
18 changed files with 1366 additions and 4 deletions
134
defects/spring/unit/SpringVersionResourceResolverTest.java
Normal file
134
defects/spring/unit/SpringVersionResourceResolverTest.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package unit;
|
||||
|
||||
/**
|
||||
* Regression test for spring-0006: CWE-407 O(N²) patternsList.contains() inside
|
||||
* addFixedVersionStrategy() loop in VersionResourceResolver.
|
||||
*
|
||||
* File: spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java
|
||||
* Lines: 130-141
|
||||
*
|
||||
* Defect: patternsList = Arrays.asList(pathPatterns) then for each pattern calls
|
||||
* patternsList.contains(versionPrefix + pattern) — O(N) inside O(N) loop = O(N²).
|
||||
*
|
||||
* Fix: use HashSet<String> for O(1) membership check.
|
||||
*/
|
||||
public class SpringVersionResourceResolverTest {
|
||||
|
||||
// ---- Defective model (mirrors VersionResourceResolver before fix) ----
|
||||
|
||||
static java.util.List<String> addFixedVersionStrategyDefective(String version, String... pathPatterns) {
|
||||
java.util.List<String> patternsList = java.util.Arrays.asList(pathPatterns); // plain List — O(N) contains
|
||||
java.util.List<String> prefixedPatterns = new java.util.ArrayList<>(pathPatterns.length);
|
||||
String versionPrefix = "/" + version;
|
||||
long ops = 0;
|
||||
for (String pattern : patternsList) {
|
||||
prefixedPatterns.add(pattern);
|
||||
ops++;
|
||||
for (String p : patternsList) { ops++; } // simulate contains() scan
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
slowOps = ops;
|
||||
return prefixedPatterns;
|
||||
}
|
||||
|
||||
// ---- Fixed model (mirrors VersionResourceResolver after fix) ----
|
||||
|
||||
static java.util.List<String> addFixedVersionStrategyFixed(String version, String... pathPatterns) {
|
||||
java.util.Set<String> patternsSet = new java.util.HashSet<>(java.util.Arrays.asList(pathPatterns));
|
||||
java.util.List<String> prefixedPatterns = new java.util.ArrayList<>(pathPatterns.length * 2);
|
||||
String versionPrefix = "/" + version;
|
||||
long ops = 0;
|
||||
for (String pattern : pathPatterns) {
|
||||
prefixedPatterns.add(pattern);
|
||||
ops++;
|
||||
ops++; // HashSet.contains() = O(1)
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsSet.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
fastOps = ops;
|
||||
return prefixedPatterns;
|
||||
}
|
||||
|
||||
static long slowOps = 0;
|
||||
static long fastOps = 0;
|
||||
|
||||
// ---- Tests ----
|
||||
|
||||
public static void main(String[] args) {
|
||||
testCorrectness();
|
||||
testNoFalseDuplicates();
|
||||
testPerformance();
|
||||
System.out.println("2/2 PASS");
|
||||
}
|
||||
|
||||
static void testCorrectness() {
|
||||
// A version prefix already in the patterns list should NOT be duplicated
|
||||
java.util.List<String> result = addFixedVersionStrategyFixed("1.0.0",
|
||||
"/js/**", "/css/**", "/1.0.0/js/**");
|
||||
|
||||
// "/js/**" already has versioned form in list — should not add "/1.0.0/js/**" again
|
||||
long count = result.stream().filter(p -> p.equals("/1.0.0/js/**")).count();
|
||||
if (count != 1) {
|
||||
System.err.println("FAIL testCorrectness: expected 1 occurrence of /1.0.0/js/**, got " + count);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// "/css/**" has no versioned form yet — should add "/1.0.0/css/**"
|
||||
count = result.stream().filter(p -> p.equals("/1.0.0/css/**")).count();
|
||||
if (count != 1) {
|
||||
System.err.println("FAIL testCorrectness: expected 1 occurrence of /1.0.0/css/**, got " + count);
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] correctness: versioned patterns deduped correctly");
|
||||
}
|
||||
|
||||
static void testNoFalseDuplicates() {
|
||||
// Both implementations should produce same result
|
||||
String[] patterns = {"/a/**", "/b/**", "/c/**", "/1.5/a/**"};
|
||||
java.util.List<String> defective = addFixedVersionStrategyDefective("1.5", patterns);
|
||||
java.util.List<String> fixed = addFixedVersionStrategyFixed("1.5", patterns);
|
||||
|
||||
if (defective.size() != fixed.size()) {
|
||||
System.err.println("FAIL testNoFalseDuplicates: defective=" + defective.size()
|
||||
+ " fixed=" + fixed.size() + " — results differ");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] no false duplicates: defective=" + defective.size()
|
||||
+ " fixed=" + fixed.size());
|
||||
}
|
||||
|
||||
static void testPerformance() {
|
||||
final int N = 2000;
|
||||
String[] patterns = new String[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
patterns[i] = "/path" + i + "/**";
|
||||
}
|
||||
|
||||
// Defective: O(N²) operations
|
||||
long t0 = System.nanoTime();
|
||||
addFixedVersionStrategyDefective("v1.0", patterns);
|
||||
long slowTime = System.nanoTime() - t0;
|
||||
long slowCount = slowOps;
|
||||
|
||||
// Fixed: O(N) operations
|
||||
t0 = System.nanoTime();
|
||||
addFixedVersionStrategyFixed("v1.0", patterns);
|
||||
long fastTime = System.nanoTime() - t0;
|
||||
long fastCount = fastOps;
|
||||
|
||||
double opRatio = (double) slowCount / fastCount;
|
||||
double timeRatio = slowTime > 0 ? (double) slowTime / fastTime : 1.0;
|
||||
|
||||
System.out.printf(" [PERF] N=%d slow_ops=%d fast_ops=%d op_ratio=%.1fx time_ratio=%.1fx%n",
|
||||
N, slowCount, fastCount, opRatio, timeRatio);
|
||||
|
||||
if (opRatio < 5.0) {
|
||||
System.err.println("FAIL testPerformance: op ratio " + opRatio + "x < 5x minimum");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println(" [PASS] performance: " + opRatio + "x op ratio (>= 5x required)");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue