|
37 | 37 |
|
38 | 38 | describe "versions" do |
39 | 39 | it "assumes that future versions will be patched" do |
| 40 | + patched_versions = advisory['patched_versions'] || [] |
40 | 41 | unaffected_versions = advisory['unaffected_versions'] || [] |
41 | | - patched_versions = advisory['patched_versions'] || [] |
42 | | - |
43 | | - versions = (unaffected_versions + patched_versions).sort_by do |v| |
44 | | - Gem::Version.new(v.match(/[0-9.]+\.\d+/)[0]) |
45 | | - end |
46 | 42 |
|
47 | 43 | # If a gem is unpatched this test makes no sense |
48 | 44 | unless patched_versions.none? |
49 | | - expect(versions.last).to match(/^(?:>=|>) /) |
| 45 | + # Sort only patched versions and check if the highest one indicates future versions are patched |
| 46 | + sorted_patched_versions = patched_versions.sort_by do |v| |
| 47 | + # Extract version number more robustly |
| 48 | + version_match = v.match(/([0-9]+(?:\.[0-9]+)*(?:\.[a-zA-Z0-9]+)*)/) |
| 49 | + if version_match |
| 50 | + begin |
| 51 | + Gem::Version.new(version_match[1]) |
| 52 | + rescue ArgumentError |
| 53 | + # If version parsing fails, use the original string for sorting |
| 54 | + Gem::Version.new("0.0.0") |
| 55 | + end |
| 56 | + else |
| 57 | + Gem::Version.new("0.0.0") |
| 58 | + end |
| 59 | + end |
| 60 | + |
| 61 | + # The highest patched version should indicate that future versions are also patched |
| 62 | + # This means it should use >= or > operators, or contain >= in compound requirements |
| 63 | + # UNLESS there are unaffected_versions that indicate the vulnerability doesn't exist in newer versions |
| 64 | + highest_patched = sorted_patched_versions.last |
| 65 | + |
| 66 | + # Check if there are unaffected versions that are higher than the patched versions |
| 67 | + # This indicates the vulnerability was fixed in a specific range but doesn't exist in newer versions |
| 68 | + has_higher_unaffected = false |
| 69 | + unless unaffected_versions.empty? |
| 70 | + unaffected_versions.each do |unaffected| |
| 71 | + if unaffected.match(/^>=?\s*([0-9]+(?:\.[0-9]+)*)/) |
| 72 | + # This indicates newer versions are unaffected, so the test doesn't apply |
| 73 | + has_higher_unaffected = true |
| 74 | + break |
| 75 | + end |
| 76 | + end |
| 77 | + end |
| 78 | + |
| 79 | + # Skip the test if there are higher unaffected versions |
| 80 | + unless has_higher_unaffected |
| 81 | + # Check if the version requirement indicates future versions are patched |
| 82 | + # This can be: ">= x.y.z", "> x.y.z", or compound like "~> x.y.z, >= x.y.z.w" |
| 83 | + future_versions_patched = highest_patched.match(/^(?:>=|>) /) || |
| 84 | + highest_patched.include?(', >=') || |
| 85 | + highest_patched.include?(', >') |
| 86 | + |
| 87 | + expect(future_versions_patched).to be_truthy, |
| 88 | + "Expected highest patched version '#{highest_patched}' to indicate future versions are patched (should use >=, >, or compound requirement with >=)" |
| 89 | + end |
50 | 90 | end |
51 | 91 | end |
52 | 92 | end |
|
0 commit comments