selenium-0001: SessionCapabilitiesMutator list.contains O(NxM) -> LinkedHashSet O(N+M). Grid Node session mutation hot path. Bench: 192x at N=M=1000. selenium-0002: ChromiumOptions merge helpers consolidate four list.contains loops behind addArgumentsUnique/addEncodedExtensionsUnique. Bench: 254x at N=M=1000. playwright-0001: roleUtils validRoles / allowsNameFromContent Array.includes on 20-70 element constant arrays per element. Converted to Set<string> at module load. Bench: 11x at N=10000 elements. webdriverio-0001: xpath-conditions extractOrConditions orMatches.find + values.includes per regex match -> Map<attr, Set<values>>. Bench: 6x at K=V=60 in the 'mobileSelectorPerformanceOptimizer'. Each defect ships: ticket, patch with complexity-gate header, Python benchmark + correctness test, Makefile, outreach brief. All 16 tests pass. UNDF IDs: 1276 (playwright), 1277 (selenium-0001), 1288 (selenium-0002), 1289 (webdriverio).
92 lines
3.8 KiB
Diff
92 lines
3.8 KiB
Diff
# UNDF: UNDF-2026-000001277
|
|
# UNDF: UNDF-2026-XXXXXXXXX
|
|
# CWE-407: Algorithmic Complexity -- O(N*M) -> O(N+M) in SessionCapabilitiesMutator
|
|
#
|
|
# Defect: Grid Node session mutator deduplicates args/extensions from client caps
|
|
# against slot stereotype caps via ArrayList.contains inside forEach. That
|
|
# runs O(M) per iteration across N items. Chromium and Firefox merge paths
|
|
# both carry this pattern; Chromium path also applies it to extensions.
|
|
#
|
|
# Fix: Pre-build a LinkedHashSet from the existing stereotype list once, then
|
|
# iterate the incoming list with set.add() which returns true only on first
|
|
# insertion. LinkedHashSet preserves insertion order, preserving the original
|
|
# semantics. Cost drops to O(N+M) per merged list.
|
|
#
|
|
# Complexity gate (tests/test-selenium-cwe407.py):
|
|
# k-scaling 5x: time ratio must be <17.5x (O(k) ~=5x, not O(k^2) ~=25x)
|
|
# N=M=20 per session, merge: must complete in <1ms
|
|
--- a/java/src/org/openqa/selenium/grid/node/config/SessionCapabilitiesMutator.java
|
|
+++ b/java/src/org/openqa/selenium/grid/node/config/SessionCapabilitiesMutator.java
|
|
@@ -19,10 +19,12 @@ package org.openqa.selenium.grid.node.config;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
+import java.util.LinkedHashSet;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
+import java.util.Set;
|
|
import java.util.function.Function;
|
|
import org.openqa.selenium.Capabilities;
|
|
import org.openqa.selenium.ImmutableCapabilities;
|
|
@@ -133,12 +135,15 @@ public class SessionCapabilitiesMutator implements Function<Capabilities, Capabi
|
|
new ArrayList<>(
|
|
(List<String>) (stereotypeOptions.getOrDefault(("args"), new ArrayList<>())));
|
|
|
|
- arguments.forEach(
|
|
- arg -> {
|
|
- if (!stereotypeArguments.contains(arg)) {
|
|
- stereotypeArguments.add(arg);
|
|
- }
|
|
- });
|
|
+ // Pre-build a Set from the existing list for O(1) dedup (was O(M) per iteration,
|
|
+ // totaling O(N*M) across the forEach). LinkedHashSet preserves insertion order
|
|
+ // so downstream consumers see the same args sequence as the prior implementation.
|
|
+ Set<String> seenArgs = new LinkedHashSet<>(stereotypeArguments);
|
|
+ for (String arg : arguments) {
|
|
+ if (seenArgs.add(arg)) {
|
|
+ stereotypeArguments.add(arg);
|
|
+ }
|
|
+ }
|
|
toReturn.put("args", stereotypeArguments);
|
|
}
|
|
|
|
@@ -151,12 +156,12 @@ public class SessionCapabilitiesMutator implements Function<Capabilities, Capabi
|
|
new ArrayList<>(
|
|
(List<String>) (stereotypeOptions.getOrDefault(("extensions"), new ArrayList<>())));
|
|
|
|
- extensionList.forEach(
|
|
- extension -> {
|
|
- if (!stereotypeExtensions.contains(extension)) {
|
|
- stereotypeExtensions.add(extension);
|
|
- }
|
|
- });
|
|
+ Set<String> seenExtensions = new LinkedHashSet<>(stereotypeExtensions);
|
|
+ for (String extension : extensionList) {
|
|
+ if (seenExtensions.add(extension)) {
|
|
+ stereotypeExtensions.add(extension);
|
|
+ }
|
|
+ }
|
|
|
|
toReturn.put("extensions", stereotypeExtensions);
|
|
}
|
|
@@ -191,12 +196,12 @@ public class SessionCapabilitiesMutator implements Function<Capabilities, Capabi
|
|
new ArrayList<>(
|
|
(List<String>) (stereotypeOptions.getOrDefault(("args"), new ArrayList<>())));
|
|
|
|
- arguments.forEach(
|
|
- arg -> {
|
|
- if (!stereotypeArguments.contains(arg)) {
|
|
- stereotypeArguments.add(arg);
|
|
- }
|
|
- });
|
|
+ Set<String> seenArgs = new LinkedHashSet<>(stereotypeArguments);
|
|
+ for (String arg : arguments) {
|
|
+ if (seenArgs.add(arg)) {
|
|
+ stereotypeArguments.add(arg);
|
|
+ }
|
|
+ }
|
|
toReturn.put("args", stereotypeArguments);
|
|
}
|
|
|