|
1 |
| -require 'json' |
2 |
| -require 'versionomy' |
| 1 | +require "bundler/audit/scanner" |
| 2 | +require "json" |
| 3 | +require "versionomy" |
| 4 | + |
| 5 | +require "cc/engine/bundler_audit/analyzer" |
| 6 | +require "cc/engine/bundler_audit/issue" |
| 7 | +require "cc/engine/bundler_audit/remediation" |
3 | 8 |
|
4 | 9 | module CC
|
5 | 10 | module Engine
|
6 |
| - class BundlerAudit |
7 |
| - GemfileLockNotFound = Class.new(StandardError) |
8 |
| - SEVERITIES = { |
9 |
| - "High" => "critical", |
10 |
| - "Low" => "info", |
11 |
| - "Medium" => "normal", |
12 |
| - } |
13 |
| - |
14 |
| - def initialize(directory: , io: , engine_config: ) |
15 |
| - @directory = directory |
16 |
| - @engine_config = engine_config |
17 |
| - @io = io |
18 |
| - end |
19 |
| - |
20 |
| - def run |
21 |
| - if gemfile_lock_exists? |
22 |
| - Dir.chdir(@directory) |
23 |
| - raw_output = `bundle-audit` |
24 |
| - raw_issues = raw_output.split(/\n\n/).select { |chunk| |
25 |
| - chunk =~ /^Name: / |
26 |
| - } |
27 |
| - @gemfile_lock_lines = File.read( |
28 |
| - File.join(@directory, 'Gemfile.lock') |
29 |
| - ).lines |
30 |
| - raw_issues.each do |raw_issue| |
31 |
| - issue = issue_from_raw(raw_issue) |
32 |
| - @io.print("#{issue.to_json}\0") |
33 |
| - end |
34 |
| - else |
35 |
| - raise GemfileLockNotFound, "No Gemfile.lock found." |
36 |
| - end |
37 |
| - end |
38 |
| - |
39 |
| - private |
40 |
| - |
41 |
| - def gemfile_lock_exists? |
42 |
| - File.exist?(File.join(@directory, 'Gemfile.lock')) |
43 |
| - end |
44 |
| - |
45 |
| - def issue_from_raw(raw_issue) |
46 |
| - raw_issue_hash = {} |
47 |
| - raw_issue.lines.each do |l| |
48 |
| - l =~ /^([^:]+): (.+)\n?/ |
49 |
| - raw_issue_hash[$1] = $2 |
50 |
| - end |
51 |
| - line_number = nil |
52 |
| - @gemfile_lock_lines.each_with_index do |l, i| |
53 |
| - if l =~ /^\s*#{raw_issue_hash['Name']} \([\d.]+\)/ |
54 |
| - line_number = i + 1 |
55 |
| - end |
56 |
| - end |
57 |
| - { |
58 |
| - categories: ['Security'], |
59 |
| - check_name: "Insecure Dependency", |
60 |
| - content: { |
61 |
| - body: content_body(raw_issue_hash) |
62 |
| - }, |
63 |
| - description: raw_issue_hash['Title'], |
64 |
| - location: { |
65 |
| - path: 'Gemfile.lock', |
66 |
| - lines: { |
67 |
| - begin: line_number, |
68 |
| - end: line_number |
69 |
| - } |
70 |
| - }, |
71 |
| - remediation_points: remediation_points( |
72 |
| - raw_issue_hash['Version'], raw_issue_hash['Solution'] |
73 |
| - ), |
74 |
| - severity: SEVERITIES[raw_issue_hash["Criticality"]], |
75 |
| - type: 'Issue', |
76 |
| - } |
77 |
| - end |
78 |
| - |
79 |
| - def remediation_points(current_version, raw_solution) |
80 |
| - if raw_solution =~ /^upgrade to (.*)/ |
81 |
| - raw_upgrades = $1.scan(/\d+\.\d+\.\d+/) |
82 |
| - current_version = Versionomy.parse(current_version) |
83 |
| - result = 5_000_000_000 |
84 |
| - raw_upgrades.each do |raw_upgrade| |
85 |
| - upgrade_version = Versionomy.parse(raw_upgrade) |
86 |
| - if upgrade_version > current_version |
87 |
| - points_this_upgrade = nil |
88 |
| - if current_version.major == upgrade_version.major |
89 |
| - if current_version.minor == upgrade_version.minor |
90 |
| - points_this_upgrade = 500_000 # patch upgrade |
91 |
| - else |
92 |
| - points_this_upgrade = 5_000_000 # minor upgrade |
93 |
| - end |
94 |
| - else |
95 |
| - points_this_upgrade = 50_000_000 # major upgrade |
96 |
| - end |
97 |
| - result = points_this_upgrade if points_this_upgrade < result |
98 |
| - end |
99 |
| - end |
100 |
| - result |
101 |
| - else |
102 |
| - 500_000_000 # No upgrade of gem possible |
103 |
| - end |
104 |
| - end |
105 |
| - |
106 |
| - def content_body(raw_issue_hash) |
107 |
| - %w[Advisory Criticality URL Solution].map do |key| |
108 |
| - "**#{key}**: #{raw_issue_hash[key]}" |
109 |
| - end.join("\n\n") |
110 |
| - end |
| 11 | + module BundlerAudit |
111 | 12 | end
|
112 | 13 | end
|
113 | 14 | end
|
114 |
| - |
0 commit comments