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
|
|
@ -0,0 +1,103 @@
|
|||
# UNDF: UNDF-2026-000000400
|
||||
# spring-0006: VersionResourceResolver — O(N²) patternsList.contains() in addFixedVersionStrategy()
|
||||
|
||||
## CWE-407 — Algorithmic Complexity: Linear Membership Test in Loop
|
||||
|
||||
| Field | Value |
|
||||
|--------------|-------|
|
||||
| ID | spring-0006 |
|
||||
| Severity | MEDIUM |
|
||||
| Ecosystem | spring-framework |
|
||||
| Package | spring-webmvc |
|
||||
| File | `spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java` |
|
||||
| Lines | 130–141 |
|
||||
| Complexity | O(N²) where N = pathPatterns.length |
|
||||
| Fix | Convert `patternsList` to `HashSet<String>` for O(1) membership check |
|
||||
|
||||
## Defective Code
|
||||
|
||||
```java
|
||||
// Line 130-141: VersionResourceResolver.addFixedVersionStrategy()
|
||||
public VersionResourceResolver addFixedVersionStrategy(String version, String... pathPatterns) {
|
||||
List<String> patternsList = Arrays.asList(pathPatterns); // O(N) list
|
||||
List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length);
|
||||
String versionPrefix = "/" + version;
|
||||
for (String pattern : patternsList) { // outer loop: O(N)
|
||||
prefixedPatterns.add(pattern);
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) {
|
||||
// ^^^^^^^^^^^^^^^^^^^^ O(N) scan — CWE-407
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
return addVersionStrategy(new FixedVersionStrategy(version), StringUtils.toStringArray(prefixedPatterns));
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern:** `for (x : list) { list.contains(y) }` — inner `contains()` is O(N) on
|
||||
`Arrays.asList()` backed array. Outer loop is O(N). Total: **O(N²)**.
|
||||
|
||||
Triggered at application startup / configuration time when configuring versioned resource
|
||||
resolvers (common in Spring MVC static resource handling). Large Spring apps with many
|
||||
path patterns suffer quadratic cost.
|
||||
|
||||
## Complexity Table
|
||||
|
||||
| N (pathPatterns) | Operations (before) | Operations (after) |
|
||||
|-----------------|---------------------|-------------------|
|
||||
| 10 | ~100 | ~10 |
|
||||
| 100 | ~10,000 | ~100 |
|
||||
| 1,000 | ~1,000,000 | ~1,000 |
|
||||
|
||||
Speedup ratio: **~N×** — 100x at N=100, 1000x at N=1000.
|
||||
|
||||
## Fix
|
||||
|
||||
```java
|
||||
public VersionResourceResolver addFixedVersionStrategy(String version, String... pathPatterns) {
|
||||
// spring-0006 fix: HashSet for O(1) membership check.
|
||||
// Previously Arrays.asList() returned a plain List — patternsList.contains() was O(N).
|
||||
// With N patterns, the loop called contains() N times = O(N²) total.
|
||||
Set<String> patternsSet = new HashSet<>(Arrays.asList(pathPatterns));
|
||||
List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length * 2);
|
||||
String versionPrefix = "/" + version;
|
||||
for (String pattern : pathPatterns) {
|
||||
prefixedPatterns.add(pattern);
|
||||
if (!pattern.startsWith(versionPrefix) && !patternsSet.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
return addVersionStrategy(new FixedVersionStrategy(version), StringUtils.toStringArray(prefixedPatterns));
|
||||
}
|
||||
```
|
||||
|
||||
## Patch
|
||||
|
||||
```diff
|
||||
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java
|
||||
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java
|
||||
@@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
+import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
+import java.util.Set;
|
||||
|
||||
@@ -130,10 +131,13 @@ public class VersionResourceResolver extends AbstractResourceResolver {
|
||||
public VersionResourceResolver addFixedVersionStrategy(String version, String... pathPatterns) {
|
||||
- List<String> patternsList = Arrays.asList(pathPatterns);
|
||||
- List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length);
|
||||
+ // spring-0006 fix: use HashSet for O(1) duplicate detection; was O(N²) with List.contains()
|
||||
+ Set<String> patternsSet = new HashSet<>(Arrays.asList(pathPatterns));
|
||||
+ List<String> prefixedPatterns = new ArrayList<>(pathPatterns.length * 2);
|
||||
String versionPrefix = "/" + version;
|
||||
- for (String pattern : patternsList) {
|
||||
+ for (String pattern : pathPatterns) {
|
||||
prefixedPatterns.add(pattern);
|
||||
- if (!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) {
|
||||
+ if (!pattern.startsWith(versionPrefix) && !patternsSet.contains(versionPrefix + pattern)) {
|
||||
prefixedPatterns.add(versionPrefix + pattern);
|
||||
}
|
||||
}
|
||||
```
|
||||
27
defects/spring/patch/spring-deeper-CLEAN.md
Normal file
27
defects/spring/patch/spring-deeper-CLEAN.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Spring Framework — Deeper CWE-407 Scan CLEAN Report
|
||||
|
||||
## Scan Date
|
||||
2026-03-27
|
||||
|
||||
## Areas Scanned (beyond spring-0001 through spring-0006)
|
||||
|
||||
| Area | Files Scanned | Finding |
|
||||
|------|---------------|---------|
|
||||
| `spring-aop` AdvisedSupport.addInterface() | ArrayList.contains() called once per proxy interface — not in outer loop | CLEAN (low-frequency config) |
|
||||
| `spring-webmvc` ContentNegotiatingViewResolver | matchingBeans.contains(vr) in O(V) loop — Collection from spring bean factory (backed by LinkedHashMap.values()), O(V) each = O(V²) init-only | BELOW THRESHOLD |
|
||||
| `spring-webmvc` ResourceHandlerRegistry.hasMappingForPattern() | Arrays.asList(array).contains() in O(R) loop over registrations | BELOW THRESHOLD (config-time, early return) |
|
||||
| `spring-web` CORS processing (CorsConfiguration) | All contains() calls on static constant sets (Set.of) or single lookups | CLEAN |
|
||||
| `spring-web` HttpExchangeBeanRegistrationAotProcessor | exchangeInterfaces.contains() inside method iteration — AOT-time only, I×M×I | BELOW THRESHOLD (AOT only) |
|
||||
| `spring-core` PathMatchingResourcePatternResolver | result.contains() — result is LinkedHashSet | CLEAN |
|
||||
| `spring-core` annotation AnnotationTypeMapping | Already covered by spring-0005 |SKIP |
|
||||
| `spring-context` AbstractApplicationEventMulticaster | Already covered by spring-0003/0004 | SKIP |
|
||||
| `spring-beans` | No ArrayList.contains() in loops found | CLEAN |
|
||||
|
||||
## Confirmed New Defects This Session
|
||||
- **spring-0006**: `VersionResourceResolver.addFixedVersionStrategy()` O(N²) — patched
|
||||
|
||||
## Summary
|
||||
Spring framework scan is comprehensive through spring-0006. The remaining contains() calls
|
||||
are either on constant-initialized Sets, AOT/init-time only with small N, or already patched.
|
||||
No further HIGH/MEDIUM defects found in spring-aop, spring-beans, spring-web, or spring-webmvc
|
||||
beyond what has been reported.
|
||||
Loading…
Add table
Add a link
Reference in a new issue