Class: RailsCodeAuditor::ReportGenerator
- Inherits:
-
Object
- Object
- RailsCodeAuditor::ReportGenerator
- Defined in:
- lib/rails_code_auditor/report_generator.rb
Class Method Summary collapse
- .normalize(results) ⇒ Object
- .parse_json_input(input, label: "JSON") ⇒ Object
- .summarize_brakeman(raw) ⇒ Object
- .summarize_bundler(raw) ⇒ Object
- .summarize_fasterer(raw) ⇒ Object
- .summarize_license_finder(raw) ⇒ Object
- .summarize_or_skip(tool, results) ⇒ Object
- .summarize_rails_best_practices(raw) ⇒ Object
- .summarize_reek(raw) ⇒ Object
- .summarize_rubocop(raw) ⇒ Object
- .summarize_rubycritic(raw) ⇒ Object
- .summarize_text_tool(name, raw) ⇒ Object
Class Method Details
.normalize(results) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/rails_code_auditor/report_generator.rb', line 3 def self.normalize(results) { brakeman: summarize_or_skip(:brakeman, results) { |res| summarize_brakeman(res[:json]) }, bundler_audit: summarize_or_skip(:bundler_audit, results) { |res| summarize_bundler(res[:json]) }, rubocop: summarize_or_skip(:rubocop, results) { |res| summarize_rubocop(res[:json]) }, rails_best_practices: summarize_or_skip(:rails_best_practices, results) do |res| summarize_rails_best_practices(res[:json]) end, flay: summarize_or_skip(:flay, results) { |res| summarize_text_tool("Flay", res[:text]) }, flog: summarize_or_skip(:flog, results) { |res| summarize_text_tool("Flog", res[:text]) }, license_finder: summarize_or_skip(:license_finder, results) { |res| summarize_license_finder(res[:json]) }, reek: summarize_or_skip(:reek, results) { |res| summarize_reek(res[:json]) }, rubycritic: summarize_or_skip(:rubycritic, results) { |res| summarize_rubycritic(res[:json]) }, fasterer: summarize_or_skip(:fasterer, results) { |res| summarize_fasterer(res[:text]) } } end |
.parse_json_input(input, label: "JSON") ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/rails_code_auditor/report_generator.rb', line 46 def self.parse_json_input(input, label: "JSON") case input when String begin JSON.parse(input) rescue JSON::ParserError => e warn "❌ Failed to parse #{label} string: #{e.}" {} end when Hash input else warn "❌ Unsupported #{label} input type: #{input.class}" {} end end |
.summarize_brakeman(raw) ⇒ Object
36 37 38 39 40 41 42 43 44 |
# File 'lib/rails_code_auditor/report_generator.rb', line 36 def self.summarize_brakeman(raw) json = parse_json_input(raw, label: "Brakeman") warnings = json["warnings"] || [] summary = warnings.map { |w| "#{w["warning_type"]}: #{w["message"]} in #{w["file"]}" }.join("\n") { status: "#{warnings.size} security warning#{"s" unless warnings.size == 1}", details: summary } end |
.summarize_bundler(raw) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/rails_code_auditor/report_generator.rb', line 63 def self.summarize_bundler(raw) json = begin JSON.parse(raw) rescue StandardError {} end vulns = json["advisories"] || [] details = vulns.map { |v| "#{v["gem"]}: #{v["title"]}" }.join("\n") { status: "#{vulns.size} vulnerability#{"ies" unless vulns.size == 1}", details: details } end |
.summarize_fasterer(raw) ⇒ Object
167 168 169 170 171 172 173 |
# File 'lib/rails_code_auditor/report_generator.rb', line 167 def self.summarize_fasterer(raw) suggestions = raw.lines.select { |line| line.include?(":") } { status: "#{suggestions.size} performance suggestion#{"s" unless suggestions.size == 1}", details: suggestions.join("\n") } end |
.summarize_license_finder(raw) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/rails_code_auditor/report_generator.rb', line 119 def self.summarize_license_finder(raw) json = begin JSON.parse(raw) rescue StandardError [] end problematic = json.select { |pkg| pkg["approved"] == false } details = problematic.map { |p| "#{p["name"]} - #{p["licenses"].join(", ")}" }.join("\n") { status: "#{problematic.size} unapproved license#{"s" unless problematic.size == 1}", details: details } end |
.summarize_or_skip(tool, results) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/rails_code_auditor/report_generator.rb', line 20 def self.summarize_or_skip(tool, results) if results[tool]&.dig(:skipped) { status: "Skipped", details: results[tool][:reason] || "Tool not available in this environment" } elsif results[tool].nil? { status: "Not Run", details: "No data available for #{tool}" } else yield(results[tool]) end end |
.summarize_rails_best_practices(raw) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/rails_code_auditor/report_generator.rb', line 91 def self.summarize_rails_best_practices(raw) issues = begin JSON.parse(raw) rescue StandardError [] end status = "#{issues.size} issue#{"s" unless issues.size == 1}" grouped = issues.group_by { |issue| issue["message"] } details = grouped.map do |, group| "#{} (#{group.size}x)" end.join("\n") { status: status, details: details } end |
.summarize_reek(raw) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/rails_code_auditor/report_generator.rb', line 133 def self.summarize_reek(raw) parsed = raw.is_a?(String) ? JSON.parse(raw, symbolize_names: true) : raw puts "JSON array but got #{parsed.class}" unless parsed.is_a?(Array) total_smells = parsed.size sample_smells = parsed.first(10) details = sample_smells.map do |smell| "#{smell["source"]} [#{smell["lines"].join(", ")}]: #{smell["message"]} (#{smell["smell_type"]})" end { status: "#{total_smells} smell#{"s" unless total_smells == 1}", details: details.join("\n") + (total_smells > 10 ? "\n..." : "") } end |
.summarize_rubocop(raw) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/rails_code_auditor/report_generator.rb', line 77 def self.summarize_rubocop(raw) json = begin JSON.parse(raw) rescue StandardError {} end offenses = json["files"]&.flat_map { |f| f["offenses"] } || [] details = offenses.map { |o| "#{o["cop_name"]}: #{o["message"]}" }.join("\n") { status: "#{offenses.size} code offense#{"s" unless offenses.size == 1}", details: details } end |
.summarize_rubycritic(raw) ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/rails_code_auditor/report_generator.rb', line 151 def self.summarize_rubycritic(raw) lines = raw.to_s.split("\n").map(&:strip).reject(&:empty?) # Extract score score_line = lines.find { |line| line.match?(/^Score:\s+\d+(\.\d+)?$/) } score = score_line&.match(/Score:\s+([\d.]+)/)&.captures&.first # Extract letter-grade issues (lines that start with a grade followed by a dash) issues = lines.select { |line| line.match?(/^\b[FABCDE]\b\s+-\s+/) } { status: score ? "Score: #{score}" : "No score found", details: issues.first(10).join("\n") + (issues.size > 10 ? "\n..." : "") } end |
.summarize_text_tool(name, raw) ⇒ Object
111 112 113 114 115 116 117 |
# File 'lib/rails_code_auditor/report_generator.rb', line 111 def self.summarize_text_tool(name, raw) lines = raw ? raw.split("\n").reject(&:empty?) : [] { status: "#{lines.size} issue#{"s" unless lines.size == 1}", details: lines.first(10).join("\n") + (lines.size > 10 ? "\n..." : "") } end |