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:
parent
b9c6ea007d
commit
33cc466b3a
9 changed files with 345 additions and 1 deletions
6
defects/vagrant/Makefile
Normal file
6
defects/vagrant/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.PHONY: all bench clean
|
||||
all: bench
|
||||
bench:
|
||||
python3 bench/run_all.py
|
||||
clean:
|
||||
rm -rf bench/__pycache__ __pycache__
|
||||
55
defects/vagrant/bench/bench-vagrant-0001.py
Normal file
55
defects/vagrant/bench/bench-vagrant-0001.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python3
|
||||
# bench-vagrant-0001.py
|
||||
# Bundler plugin loader: plugins.keys.include?(spec.name) inside a per-spec
|
||||
# loop. O(S*P) for S resolved specs and P plugins. Fix: hoist Set, O(1)
|
||||
# per spec.
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def bench_defective(s_specs, p_plugins):
|
||||
plugin_keys = [f"plugin_{i:03d}" for i in range(p_plugins)]
|
||||
specs = [f"plugin_{(i * 7) % p_plugins:03d}" for i in range(s_specs)]
|
||||
|
||||
t0 = time.perf_counter()
|
||||
matched = []
|
||||
for spec in specs:
|
||||
# Ruby Array#include? = Python list __contains__ = O(P)
|
||||
if spec in plugin_keys:
|
||||
matched.append(spec)
|
||||
return time.perf_counter() - t0
|
||||
|
||||
|
||||
def bench_fixed(s_specs, p_plugins):
|
||||
plugin_keys = [f"plugin_{i:03d}" for i in range(p_plugins)]
|
||||
specs = [f"plugin_{(i * 7) % p_plugins:03d}" for i in range(s_specs)]
|
||||
|
||||
t0 = time.perf_counter()
|
||||
plugin_set = set(plugin_keys)
|
||||
matched = []
|
||||
for spec in specs:
|
||||
if spec in plugin_set: # Set#include? = O(1)
|
||||
matched.append(spec)
|
||||
return time.perf_counter() - t0
|
||||
|
||||
|
||||
TRIALS = 3
|
||||
CASES = [(50, 50), (200, 100), (500, 200), (1000, 500), (2000, 1000)]
|
||||
|
||||
|
||||
def run():
|
||||
lines = []
|
||||
header = "=== vagrant-0001: Bundler plugin Array#include? vs Set#include? ==="
|
||||
print(header); lines.append(header)
|
||||
for s, p in CASES:
|
||||
df = min(bench_defective(s, p) for _ in range(TRIALS))
|
||||
fx = min(bench_fixed(s, p) for _ in range(TRIALS))
|
||||
speedup = (df / fx) if fx > 0 else float("inf")
|
||||
line = f"S={s:<5} P={p:<5}: defective={df*1000:.3f}ms fixed={fx*1000:.3f}ms speedup={speedup:.1f}x"
|
||||
print(line); lines.append(line); sys.stdout.flush()
|
||||
return lines
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
7
defects/vagrant/bench/results.txt
Normal file
7
defects/vagrant/bench/results.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
=== vagrant-0001: Bundler plugin Array#include? vs Set#include? ===
|
||||
S=50 P=50 : defective=0.037ms fixed=0.009ms speedup=4.3x
|
||||
S=200 P=100 : defective=0.437ms fixed=0.049ms speedup=9.0x
|
||||
S=500 P=200 : defective=2.319ms fixed=0.144ms speedup=16.1x
|
||||
S=1000 P=500 : defective=6.800ms fixed=0.115ms speedup=59.3x
|
||||
S=2000 P=1000 : defective=30.154ms fixed=0.237ms speedup=127.1x
|
||||
|
||||
19
defects/vagrant/bench/run_all.py
Normal file
19
defects/vagrant/bench/run_all.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
import importlib.util, os, sys
|
||||
BENCH_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
def load_module(filename):
|
||||
path = os.path.join(BENCH_DIR, filename)
|
||||
spec = importlib.util.spec_from_file_location("mod", path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
all_lines = []
|
||||
for fname in ["bench-vagrant-0001.py"]:
|
||||
mod = load_module(fname)
|
||||
lines = mod.run()
|
||||
all_lines.extend(lines); all_lines.append("")
|
||||
print(); sys.stdout.flush()
|
||||
out_path = os.path.join(BENCH_DIR, "results.txt")
|
||||
with open(out_path, "w") as f:
|
||||
f.write("\n".join(all_lines) + "\n")
|
||||
print(f"results written to {out_path}"); sys.stdout.flush()
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# UNDF: UNDF-2026-000001297
|
||||
# UNDF: UNDF-2026-XXXXXXXXX
|
||||
# CWE-407: Algorithmic Complexity -- O(S*P) -> O(S+P) in Vagrant Bundler plugin paths
|
||||
#
|
||||
# Defect: Two per-spec loops in lib/vagrant/bundler.rb call .include? on a
|
||||
# plain Ruby Array (plugins.keys / system_plugins), giving O(P) per spec.
|
||||
# Across S resolved specs, total O(S*P) per vagrant command run. Fires on
|
||||
# every vagrant invocation that touches the plugin path.
|
||||
#
|
||||
# Fix: Hoist a Set built from the array once before each loop. Set#include?
|
||||
# is O(1). require "set" already loaded at top of file.
|
||||
#
|
||||
# Complexity gate (tests/test-vagrant-cwe407.py):
|
||||
# S=P=500: fixed must complete in <5ms
|
||||
# k-scaling 5x: time ratio must be <17.5x
|
||||
--- a/lib/vagrant/bundler.rb
|
||||
+++ b/lib/vagrant/bundler.rb
|
||||
@@ -466,9 +466,11 @@ module Vagrant
|
||||
).uninstall_gem(spec)
|
||||
end
|
||||
|
||||
- solution.find_all do |spec|
|
||||
- plugins.keys.include?(spec.name)
|
||||
- end
|
||||
+ # Hoist plugin name lookup into a Set; previously plugins.keys.include?
|
||||
+ # was O(P) per spec, giving O(S*P) on every plugin-pruning call.
|
||||
+ plugin_name_set = Set.new(plugins.keys)
|
||||
+ solution.find_all { |spec| plugin_name_set.include?(spec.name) }
|
||||
end
|
||||
|
||||
# During the duration of the yielded block, Bundler loud output
|
||||
@@ -522,6 +524,8 @@ module Vagrant
|
||||
|
||||
if Vagrant.strict_dependency_enforcement
|
||||
@logger.debug("Enabling strict dependency enforcement")
|
||||
+ # Build a Set once for O(1) per-spec membership check.
|
||||
+ system_plugin_set = Set.new(system_plugins)
|
||||
plugin_deps += vagrant_internal_specs.map do |spec|
|
||||
# NOTE: When working within bundler, skip any system plugins and
|
||||
# default gems. However, when not within bundler (in the installer)
|
||||
@@ -530,7 +534,7 @@ module Vagrant
|
||||
# set does allow for resolving conservatively but it can't be set
|
||||
# from the public API (requires an instance variable set on the resolver
|
||||
# instance) so strict dependencies are used instead.
|
||||
- if Vagrant.in_bundler?
|
||||
- next if system_plugins.include?(spec.name)
|
||||
+ if Vagrant.in_bundler?
|
||||
+ next if system_plugin_set.include?(spec.name)
|
||||
# # If this spec is for a default plugin included in
|
||||
# # the ruby stdlib, ignore it
|
||||
next if spec.default_gem?
|
||||
Loading…
Add table
Add a link
Reference in a new issue