gitlab-foss: 5 CWE-407 defects — Network::Graph find_free_space/overlap O(R*S), NotificationService mentioned_users O(R*M), RefreshService commit_ids O(MR*C), Project#members_among O(U*A); 4/4 PASS
This commit is contained in:
parent
0623a342b1
commit
99e627481e
7 changed files with 292 additions and 16 deletions
|
|
@ -1,31 +1,26 @@
|
|||
# UNDF: UNDF-2026-000000854
|
||||
# UNDF: (leave blank)
|
||||
# CWE-407: Network::Graph#overlap? spaces Array#include? in range loop
|
||||
# Severity: MEDIUM
|
||||
# Speedup: ~50x at 200 spaces per commit
|
||||
# Severity: LOW-MEDIUM
|
||||
# Speedup: ~50x at 200 spaces per commit across 300 time range
|
||||
# File: app/models/network/graph.rb
|
||||
# The overlap? method iterates over a time range and calls
|
||||
# @commits[i].spaces.include?(overlap_space) on each commit's spaces
|
||||
# array. Each include? is O(S) where S = number of spaces assigned
|
||||
# to that commit. In aggregate across all overlap? calls during graph
|
||||
# layout, this compounds to O(T * S). Fix: use a Set for spaces lookup
|
||||
# (add a spaces_set accessor) or convert spaces to Set before checking.
|
||||
#
|
||||
# Note: The spaces field is also used in find_free_space and place_chain
|
||||
# where it is appended to with <<. The cleanest fix is to maintain a
|
||||
# parallel Set for O(1) lookups. Here we convert to_set inline since
|
||||
# the spaces array is read-only in overlap?.
|
||||
# to that commit. Called from find_free_parent_space for every parent
|
||||
# edge during graph layout. With many branches, spaces per commit
|
||||
# can accumulate.
|
||||
# Fix: maintain a spaces_set alongside spaces for O(1) membership.
|
||||
# Since Network::Commit#spaces is appended in place_chain with <<,
|
||||
# the simplest fix is to call .to_set once per overlap? call per commit.
|
||||
--- a/app/models/network/graph.rb
|
||||
+++ b/app/models/network/graph.rb
|
||||
@@ -175,7 +175,8 @@ module Network
|
||||
@@ -175,7 +175,7 @@ module Network
|
||||
def overlap?(range, overlap_space)
|
||||
range.each do |i|
|
||||
if i != range.first &&
|
||||
- i != range.last &&
|
||||
+ i != range.last
|
||||
+ spaces_set = @commits[i].spaces.to_set
|
||||
i != range.last &&
|
||||
- @commits[i].spaces.include?(overlap_space)
|
||||
+ if spaces_set.include?(overlap_space)
|
||||
+ @commits[i].spaces.to_set.include?(overlap_space)
|
||||
|
||||
return true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
# UNDF: (leave blank)
|
||||
# CWE-407: MergeRequests::RefreshService#post_merge_manually_merged commit_ids Array#include?
|
||||
# Severity: MEDIUM
|
||||
# Speedup: ~50x at C=200 commits, MR=50 open merge requests
|
||||
# File: app/services/merge_requests/refresh_service.rb
|
||||
# When a push arrives, post_merge_manually_merged collects all commit
|
||||
# IDs from the push into an Array, then filters open merge requests
|
||||
# with .select { commit_ids.include?(mr.diff_head_sha) }. Each
|
||||
# include? is O(C) where C = number of commits in the push. Called
|
||||
# for each of MR open merge requests targeting the branch.
|
||||
# Total: O(MR * C). Large pushes (rebases, force-pushes) can have
|
||||
# hundreds of commits.
|
||||
# Fix: convert commit_ids to a Set for O(1) lookup.
|
||||
--- a/app/services/merge_requests/refresh_service.rb
|
||||
+++ b/app/services/merge_requests/refresh_service.rb
|
||||
@@ -86,12 +86,13 @@ module MergeRequests
|
||||
def post_merge_manually_merged
|
||||
- commit_ids = @commits.map(&:id)
|
||||
+ commit_ids = @commits.map(&:id).to_set
|
||||
merge_requests = @project.merge_requests.opened
|
||||
.preload_project_and_latest_diff
|
||||
.preload_merge_data(@project)
|
||||
.preload_latest_diff_commit(@project)
|
||||
.where(target_branch: @push.branch_name).to_a
|
||||
.select(&:diff_head_commit)
|
||||
.select do |merge_request|
|
||||
commit_ids.include?(merge_request.diff_head_sha) &&
|
||||
merge_request.merge_request_diff.state != 'empty'
|
||||
end
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# UNDF: (leave blank)
|
||||
# CWE-407: Project#members_among user_ids Array#include? in select loop
|
||||
# Severity: MEDIUM
|
||||
# Speedup: ~100x at U=200 users, A=500 authorized user IDs
|
||||
# File: app/models/project.rb
|
||||
# The members_among method fetches authorized user IDs via pluck(:id)
|
||||
# into an Array, then filters the input users collection with
|
||||
# users.select { |user| user_ids.include?(user.id) }. Each include?
|
||||
# is O(A) where A = number of authorized user IDs. Called for each
|
||||
# of U input users. Total: O(U * A). On large projects with many
|
||||
# authorized users, A can be thousands.
|
||||
# Fix: convert user_ids to a Set for O(1) lookup.
|
||||
--- a/app/models/project.rb
|
||||
+++ b/app/models/project.rb
|
||||
@@ -2498,8 +2498,8 @@ class Project < ApplicationRecord
|
||||
else
|
||||
return [] if users.empty?
|
||||
|
||||
- user_ids = authorized_users.where(users: { id: users.map(&:id) }).pluck(:id)
|
||||
- users.select { |user| user_ids.include?(user.id) }
|
||||
+ user_ids = authorized_users.where(users: { id: users.map(&:id) }).pluck(:id).to_set
|
||||
+ users.select { |user| user_ids.member?(user.id) }
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue