wave5: vagrant-0001 flagship + 32-target CI/CD/IaC scan survey

vagrant-0001: bundler.rb plugin loader runs Array#include? against
plugins.keys / system_plugins inside per-spec loops. O(S*P) per
vagrant command. Fix: hoist Set.new outside the loop, O(1) per
spec lookup. Bench: 127x at S=2000 P=1000.

wave5-cicd-iac-survey.md: documents 32 projects scanned across
deployment (Spinnaker, fluxcd, Argo Rollouts/Events), modern CI/CD
(Earthly, Dagger, Buck2), container runtime (containerd, crun,
skopeo, ko, kaniko, buildah), local k8s (kind, minikube, k3s),
IaC + testing (Packer, Vagrant, ansible-lint, Molecule, InSpec,
Terratest), contract/mutation testing (Pact, Stryker, mutmut,
PIT), security (Semgrep, Bandit, gosec), Java quality (Spotbugs,
Checkstyle, chart-testing).

Clean-scan honor roll +4: chart-testing, kind, ko, pact-ruby.
This commit is contained in:
russell@unturf.com 2026-04-25 10:01:56 -04:00
parent b9c6ea007d
commit 33cc466b3a
No known key found for this signature in database
9 changed files with 345 additions and 1 deletions

View file

@ -0,0 +1,72 @@
# vagrant-0001: Bundler plugin loader — O(S×P) Array#include? in loop
**Target:** hashicorp/vagrant
**Severity:** MEDIUM
**CWE:** CWE-407 (Inefficient Algorithmic Complexity)
**MOAD:** MOAD-0001 (A Sedimentary Defect)
**File:** `lib/vagrant/bundler.rb:469-471, 533-534`
**Language:** Ruby
**Status:** open
## Description
Vagrant's `Bundler` orchestrates plugin resolution and gem-spec selection on every `vagrant` command run. Two paths walk a list of resolved gem specs and check membership against a plugin/system Array via `Array#include?` (O(P) linear scan):
```ruby
# bundler.rb:469-471 — pruning the solution to declared plugins
solution.find_all do |spec|
plugins.keys.include?(spec.name)
end
# bundler.rb:533-534 — adding strict-dependency enforcement specs
plugin_deps += vagrant_internal_specs.map do |spec|
if Vagrant.in_bundler?
next if system_plugins.include?(spec.name)
next if spec.default_gem?
end
...
end
```
For S resolved specs and P plugins (plus I internal vagrant specs and S system plugins), per-run cost is O(S×P) and O(I×S). Vagrant ships ~30 internal specs by default; users with many third-party plugins push P into the hundreds.
This runs on **every vagrant command** that touches the plugin path (`vagrant up`, `vagrant ssh`, `vagrant plugin list`, etc.) — slow startup compounds across every developer interaction.
## Root Cause
Both `plugins.keys` and `system_plugins` are plain Ruby Arrays. `Array#include?` is O(N) linear scan with `==` on each entry. Inside the per-spec block, total cost scales as O(S×P).
## Fix
Convert the lookup arrays to Sets once outside the loop. `Set#include?` is O(1) via hash.
```ruby
# Hoist:
plugin_name_set = Set.new(plugins.keys)
solution.find_all do |spec|
plugin_name_set.include?(spec.name) # O(1)
end
# Same treatment for system_plugins:
system_plugins_set = Set.new(system_plugins)
plugin_deps += vagrant_internal_specs.map do |spec|
if Vagrant.in_bundler?
next if system_plugins_set.include?(spec.name) # O(1)
next if spec.default_gem?
end
...
end
```
`require "set"` is already at the top of bundler.rb (line 6). Total cost drops to O(S+P).
## Severity Note
Per-vagrant-command overhead. Impact scales linearly with plugin count × resolved spec count. Negligible for one-plugin setups, measurable for multi-plugin developer environments. Cleanup-grade priority but high-frequency — every developer pays this on every command.
## Complexity Gate
- S=P=500: fixed must complete in <5ms
- k-scaling 5×: time ratio must be <17.5×