browser-automation: 4 CWE-407 patches (selenium x2, playwright, webdriverio)
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).
This commit is contained in:
parent
134f052457
commit
9a0253e724
28 changed files with 1729 additions and 3 deletions
|
|
@ -0,0 +1,69 @@
|
|||
# UNDF: UNDF-2026-000001289
|
||||
# UNDF: UNDF-2026-XXXXXXXXX
|
||||
# CWE-407: Algorithmic Complexity -- O(M*K + M*V) -> O(M) in XPath OR extraction
|
||||
#
|
||||
# Defect: extractOrConditions walks regex matches, then for each match does:
|
||||
# 1. orMatches.find(m => m.attr === ...) O(K) scan across existing attrs
|
||||
# 2. existing.values.includes(orMatch[2]) O(V) scan across values
|
||||
# 3. existing.values.includes(orMatch[4]) O(V) scan across values
|
||||
# Three linear scans inside a per-match loop. Total O(M*K + M*V).
|
||||
#
|
||||
# Fix: Replace array-of-objects with Map<string, Set<string>>. Map lookup is
|
||||
# O(1); Set.add dedups in O(1). Emits entries in insertion order so
|
||||
# downstream OR grouping semantics match.
|
||||
#
|
||||
# Complexity gate (tests/test-webdriverio-cwe407.py):
|
||||
# k-scaling 5x: time ratio must be <17.5x (O(k) ~=5x, not O(k^2) ~=25x)
|
||||
# K=20 attrs x V=20 values per selector: must complete in <1ms
|
||||
--- a/packages/wdio-appium-service/src/mobileSelectorPerformanceOptimizer/utils/xpath-conditions.ts
|
||||
+++ b/packages/wdio-appium-service/src/mobileSelectorPerformanceOptimizer/utils/xpath-conditions.ts
|
||||
@@ -41,29 +41,28 @@ export function extractXPathConditions(xpath: string): XPathCondition[] {
|
||||
*/
|
||||
function extractOrConditions(content: string): XPathCondition[] {
|
||||
const conditions: XPathCondition[] = []
|
||||
- const orMatches: Array<{ attr: string, values: string[] }> = []
|
||||
+ // Map of attribute name -> Set of observed values. Map lookups and Set dedup
|
||||
+ // run in amortized O(1); prior Array-of-objects + .find + .includes pattern
|
||||
+ // was O(M*K) for find and O(M*V) for each includes call, giving O(M*(K+V)).
|
||||
+ const orMatches = new Map<string, Set<string>>()
|
||||
|
||||
const orPattern = /@(\w+)\s*=\s*["']([^"']+)["']\s+or\s+@(\w+)\s*=\s*["']([^"']+)["']/gi
|
||||
let orMatch: RegExpExecArray | null
|
||||
|
||||
while ((orMatch = orPattern.exec(content)) !== null) {
|
||||
if (orMatch[1] === orMatch[3]) {
|
||||
- const existing = orMatches.find(m => m.attr === orMatch![1])
|
||||
- if (existing) {
|
||||
- if (!existing.values.includes(orMatch[2])) {
|
||||
- existing.values.push(orMatch[2])
|
||||
- }
|
||||
- if (!existing.values.includes(orMatch[4])) {
|
||||
- existing.values.push(orMatch[4])
|
||||
- }
|
||||
- } else {
|
||||
- orMatches.push({
|
||||
- attr: orMatch[1],
|
||||
- values: [orMatch[2], orMatch[4]]
|
||||
- })
|
||||
+ const attr = orMatch[1]
|
||||
+ let values = orMatches.get(attr)
|
||||
+ if (!values) {
|
||||
+ values = new Set<string>()
|
||||
+ orMatches.set(attr, values)
|
||||
}
|
||||
+ values.add(orMatch[2])
|
||||
+ values.add(orMatch[4])
|
||||
}
|
||||
}
|
||||
|
||||
- for (const orMatch of orMatches) {
|
||||
- for (const value of orMatch.values) {
|
||||
+ // Map preserves insertion order, matching the prior array-of-objects order.
|
||||
+ for (const [attr, valueSet] of orMatches) {
|
||||
+ for (const value of valueSet) {
|
||||
conditions.push({
|
||||
- attribute: orMatch.attr,
|
||||
+ attribute: attr,
|
||||
operator: '=',
|
||||
value: value,
|
||||
logicalOp: 'OR'
|
||||
Loading…
Add table
Add a link
Reference in a new issue