B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
54 lines
2.5 KiB
Diff
54 lines
2.5 KiB
Diff
From: agent-blackops <blackops@unturf.com>
|
||
Date: Thu, 26 Mar 2026 00:00:00 +0000
|
||
Subject: [PATCH] api_map/constants: remove skip.to_a conversion in inner_get_constants
|
||
|
||
CWE-407: Algorithmic complexity via unnecessary Array conversion of a
|
||
Set inside a recursive method in Constants#inner_get_constants.
|
||
|
||
`skip` is already a Set throughout the call chain — it is created as
|
||
`Set.new` in `collect_and_cache` and passed into `inner_qualify` as
|
||
`Set.new` in `qualify_namespace`. However, inside `inner_get_constants`
|
||
two lines call `pin.closure.gates - skip.to_a`:
|
||
|
||
pre_fqns = resolve(pre.name, pin.closure.gates - skip.to_a) # line 262
|
||
inc_fqns = resolve(pin.name, pin.closure.gates - skip.to_a) # line 267
|
||
|
||
`skip.to_a` allocates a new Array on every call. `gates - array`
|
||
performs an O(|gates| × |skip|) set-difference by linear scan. Because
|
||
`inner_get_constants` is recursive (called for prepends, includes, and
|
||
superclass chains), the total cost is O(depth × |gates| × |skip|²) per
|
||
`collect` call.
|
||
|
||
Fix: pass `skip` directly. `Array - Set` is supported in Ruby (Set
|
||
responds to `include?` which Array#- uses internally via Enumerable),
|
||
so `gates - skip` is O(|gates|) with O(1) per-element lookup. No
|
||
`.to_a` allocation needed.
|
||
|
||
Defect-Id: solargraph-0002
|
||
Severity: MEDIUM
|
||
CWE: CWE-407 (Inefficient Algorithmic Complexity)
|
||
---
|
||
lib/solargraph/api_map/constants.rb | 4 ++--
|
||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||
|
||
diff --git a/lib/solargraph/api_map/constants.rb b/lib/solargraph/api_map/constants.rb
|
||
index xxxxxxx..yyyyyyy 100644
|
||
--- a/lib/solargraph/api_map/constants.rb
|
||
+++ b/lib/solargraph/api_map/constants.rb
|
||
@@ -258,10 +258,10 @@ module Solargraph
|
||
def inner_get_constants fqns, visibility, skip
|
||
return [] if fqns.nil? || skip.include?(fqns)
|
||
skip.add fqns
|
||
result = []
|
||
|
||
store.get_prepends(fqns).each do |pre|
|
||
- pre_fqns = resolve(pre.name, pre.closure.gates - skip.to_a)
|
||
+ pre_fqns = resolve(pre.name, pre.closure.gates - skip) # CWE-407 fix: skip is Set, no .to_a
|
||
result.concat inner_get_constants(pre_fqns, [:public], skip)
|
||
end
|
||
result.concat(store.get_constants(fqns, visibility).sort { |a, b| a.name <=> b.name })
|
||
store.get_includes(fqns).each do |pin|
|
||
- inc_fqns = resolve(pin.name, pin.closure.gates - skip.to_a)
|
||
+ inc_fqns = resolve(pin.name, pin.closure.gates - skip) # CWE-407 fix: skip is Set, no .to_a
|
||
result.concat inner_get_constants(inc_fqns, [:public], skip)
|
||
end
|