codeclimate-fede 0.85.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/bin/check +18 -0
  3. data/bin/codeclimate +21 -0
  4. data/bin/prep-release +45 -0
  5. data/bin/publish +47 -0
  6. data/bin/release +41 -0
  7. data/bin/validate-release +18 -0
  8. data/config/engines.yml +322 -0
  9. data/lib/cc/analyzer/bridge.rb +106 -0
  10. data/lib/cc/analyzer/composite_container_listener.rb +21 -0
  11. data/lib/cc/analyzer/container/result.rb +74 -0
  12. data/lib/cc/analyzer/container.rb +208 -0
  13. data/lib/cc/analyzer/container_listener.rb +9 -0
  14. data/lib/cc/analyzer/engine.rb +125 -0
  15. data/lib/cc/analyzer/engine_output.rb +74 -0
  16. data/lib/cc/analyzer/engine_output_filter.rb +36 -0
  17. data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
  18. data/lib/cc/analyzer/filesystem.rb +50 -0
  19. data/lib/cc/analyzer/formatters/formatter.rb +53 -0
  20. data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
  21. data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
  22. data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
  23. data/lib/cc/analyzer/formatters/spinner.rb +35 -0
  24. data/lib/cc/analyzer/formatters.rb +21 -0
  25. data/lib/cc/analyzer/issue.rb +69 -0
  26. data/lib/cc/analyzer/issue_sorter.rb +30 -0
  27. data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
  28. data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
  29. data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
  30. data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
  31. data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
  32. data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
  33. data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
  34. data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
  35. data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
  36. data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
  37. data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
  38. data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
  39. data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
  40. data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
  41. data/lib/cc/analyzer/issue_validations.rb +26 -0
  42. data/lib/cc/analyzer/issue_validator.rb +11 -0
  43. data/lib/cc/analyzer/location_description.rb +45 -0
  44. data/lib/cc/analyzer/logging_container_listener.rb +24 -0
  45. data/lib/cc/analyzer/measurement.rb +22 -0
  46. data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
  47. data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
  48. data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
  49. data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
  50. data/lib/cc/analyzer/measurement_validations.rb +16 -0
  51. data/lib/cc/analyzer/measurement_validator.rb +11 -0
  52. data/lib/cc/analyzer/mounted_path.rb +80 -0
  53. data/lib/cc/analyzer/raising_container_listener.rb +32 -0
  54. data/lib/cc/analyzer/source_buffer.rb +47 -0
  55. data/lib/cc/analyzer/source_extractor.rb +79 -0
  56. data/lib/cc/analyzer/source_fingerprint.rb +40 -0
  57. data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
  58. data/lib/cc/analyzer/validator.rb +38 -0
  59. data/lib/cc/analyzer.rb +50 -0
  60. data/lib/cc/cli/analyze/engine_failure.rb +11 -0
  61. data/lib/cc/cli/analyze.rb +90 -0
  62. data/lib/cc/cli/command.rb +85 -0
  63. data/lib/cc/cli/console.rb +12 -0
  64. data/lib/cc/cli/engines/engine_command.rb +15 -0
  65. data/lib/cc/cli/engines/install.rb +35 -0
  66. data/lib/cc/cli/engines/list.rb +18 -0
  67. data/lib/cc/cli/engines.rb +5 -0
  68. data/lib/cc/cli/file_store.rb +42 -0
  69. data/lib/cc/cli/global_cache.rb +47 -0
  70. data/lib/cc/cli/global_config.rb +35 -0
  71. data/lib/cc/cli/help.rb +51 -0
  72. data/lib/cc/cli/output.rb +34 -0
  73. data/lib/cc/cli/prepare.rb +98 -0
  74. data/lib/cc/cli/runner.rb +75 -0
  75. data/lib/cc/cli/validate_config.rb +84 -0
  76. data/lib/cc/cli/version.rb +16 -0
  77. data/lib/cc/cli/version_checker.rb +107 -0
  78. data/lib/cc/cli.rb +39 -0
  79. data/lib/cc/config/checks_adapter.rb +40 -0
  80. data/lib/cc/config/default_adapter.rb +54 -0
  81. data/lib/cc/config/engine.rb +41 -0
  82. data/lib/cc/config/engine_set.rb +47 -0
  83. data/lib/cc/config/json_adapter.rb +17 -0
  84. data/lib/cc/config/prepare.rb +92 -0
  85. data/lib/cc/config/validation/check_validator.rb +34 -0
  86. data/lib/cc/config/validation/engine_validator.rb +93 -0
  87. data/lib/cc/config/validation/fetch_validator.rb +78 -0
  88. data/lib/cc/config/validation/file_validator.rb +112 -0
  89. data/lib/cc/config/validation/hash_validations.rb +52 -0
  90. data/lib/cc/config/validation/json.rb +31 -0
  91. data/lib/cc/config/validation/prepare_validator.rb +40 -0
  92. data/lib/cc/config/validation/yaml.rb +66 -0
  93. data/lib/cc/config/yaml_adapter.rb +73 -0
  94. data/lib/cc/config.rb +70 -0
  95. data/lib/cc/engine_registry.rb +74 -0
  96. data/lib/cc/resolv.rb +39 -0
  97. data/lib/cc/workspace/exclusion.rb +34 -0
  98. data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
  99. data/lib/cc/workspace/path_tree/file_node.rb +31 -0
  100. data/lib/cc/workspace/path_tree.rb +49 -0
  101. data/lib/cc/workspace.rb +39 -0
  102. metadata +279 -0
@@ -0,0 +1,415 @@
1
+ require "redcarpet"
2
+
3
+ module CC
4
+ module Analyzer
5
+ module Formatters
6
+ class HTMLFormatter < Formatter # rubocop: disable Metrics/ClassLength
7
+ LANGUAGES = Hash.new { |_, ext| ext }.
8
+ merge(
9
+ # abap
10
+ # ada
11
+ "appraisals" => "ruby",
12
+ "as" => "actionscript",
13
+ "asm" => "nasm",
14
+ "bas" => "basic",
15
+ # c
16
+ "c++" => "cpp",
17
+ "capfile" => "ruby",
18
+ "cc" => "cpp",
19
+ "cfc" => "markup",
20
+ "cfm" => "markup",
21
+ "coffee" => "coffeescript",
22
+ "cp" => "cpp",
23
+ # cpp
24
+ "cr" => "crystal",
25
+ "cs" => "csharp",
26
+ "css" => %w[css css-extras],
27
+ "cu" => "cpp",
28
+ "cxx" => "cpp",
29
+ # d
30
+ # dart
31
+ # diff
32
+ "dockerfile" => "docker",
33
+ "dpr" => "pascal",
34
+ "erl" => "erlang",
35
+ "ex" => "elixir",
36
+ "f" => "fortran",
37
+ "f90" => "fortran",
38
+ "f95" => "fortran",
39
+ "feature" => "gherkin",
40
+ "for" => "fortran",
41
+ "fs" => "fsharp",
42
+ "fsi" => "fsharp",
43
+ "fsscript" => "fsharp",
44
+ "fsx" => "fsharp",
45
+ "gemfile" => "ruby",
46
+ "gemspec" => "ruby",
47
+ # glsl
48
+ # go
49
+ # groovy
50
+ "gvy" => "groovy",
51
+ "h" => "c",
52
+ "h++" => "cpp",
53
+ # haml
54
+ # handlebars
55
+ "hbr" => "handlebars",
56
+ "hh" => "cpp",
57
+ "hpp" => "cpp",
58
+ "hs" => "haskell",
59
+ "htm" => "markup",
60
+ "html" => "markup",
61
+ "hx" => "haxe",
62
+ "hxml" => "haxe",
63
+ "icn" => "icon",
64
+ "ijs" => "j",
65
+ # ini
66
+ "iol" => "jolie",
67
+ # java
68
+ "jl" => "julia",
69
+ "js" => "javascript",
70
+ # json
71
+ # jsx
72
+ "kt" => "kotlin",
73
+ "kts" => "kotlin",
74
+ # less
75
+ "lhs" => "haskell",
76
+ "lol" => "lolcode",
77
+ "lols" => "lolcode",
78
+ "ls" => "livescript",
79
+ # lua
80
+ "m" => "objective-c",
81
+ "mab" => "ruby",
82
+ # makefile
83
+ # markdown
84
+ "md" => "markdown",
85
+ # mel
86
+ "mkd" => "markdown",
87
+ "ml" => "ocaml",
88
+ "mli" => "ocaml",
89
+ "mm" => "objective-c",
90
+ # nim
91
+ # nix
92
+ "nsi" => "nsis",
93
+ "ol" => "jolie",
94
+ # oz
95
+ "pas" => "pascal",
96
+ "patch" => "diff",
97
+ "pde" => "processing",
98
+ "php" => %w[php php-extras],
99
+ "php3" => %w[php php-extras],
100
+ "php4" => %w[php php-extras],
101
+ "php5" => %w[php php-extras],
102
+ "phtml" => %w[php php-extras],
103
+ "pl" => "perl",
104
+ "pp" => "puppet",
105
+ "prawn" => "ruby",
106
+ "pro" => "prolog",
107
+ # properties
108
+ # pure
109
+ "py" => "python",
110
+ "py3" => "python",
111
+ "pyw" => "python",
112
+ "q" => "qore",
113
+ "qm" => "qore",
114
+ "qtest" => "qore",
115
+ # r
116
+ "rake" => "ruby",
117
+ "rakefile" => "ruby",
118
+ "rantfile" => "ruby",
119
+ "rb" => "ruby",
120
+ "rbw" => "ruby",
121
+ "rjs" => "ruby",
122
+ "rpdf" => "ruby",
123
+ "rs" => "rust",
124
+ "rst" => "rest",
125
+ "ru" => "ruby",
126
+ "rxml" => "ruby",
127
+ # sass
128
+ "sc" => "scala",
129
+ # scala
130
+ "scs" => "scheme",
131
+ # scss
132
+ "shader" => "glsl",
133
+ # sql
134
+ "ss" => "scheme",
135
+ "st" => "smalltalk",
136
+ "styl" => "stylus",
137
+ # swift
138
+ # tcl
139
+ "template" => "json",
140
+ "tex" => "latex",
141
+ # textile
142
+ "tmproj" => "markup",
143
+ "tpl" => "smarty",
144
+ "ts" => "typescript",
145
+ "v" => "verilog",
146
+ "vagrantfile" => "ruby",
147
+ "vhd" => "vhdl",
148
+ # vim
149
+ "xaml" => "markup",
150
+ "xhtml" => "markup",
151
+ "xml" => "markup",
152
+ # yaml
153
+ "yaws" => "erlang",
154
+ "yml" => "yaml",
155
+ ).freeze
156
+
157
+ class Location
158
+ CONTEXT_LINES = 2
159
+ MAX_LINES = 10
160
+
161
+ def initialize(source_buffer, location)
162
+ @source_buffer = source_buffer
163
+ @location = location
164
+ end
165
+
166
+ def begin_line
167
+ @begin_line ||= line("begin")
168
+ end
169
+
170
+ def end_line
171
+ @end_line ||= line("end")
172
+ end
173
+
174
+ def to_s
175
+ [
176
+ begin_line,
177
+ end_line,
178
+ ].uniq.join("-")
179
+ end
180
+
181
+ def start
182
+ [begin_line - CONTEXT_LINES, 1].max
183
+ end
184
+
185
+ def line_offset
186
+ start - 1
187
+ end
188
+
189
+ def code
190
+ first_line = start
191
+ last_line = [
192
+ end_line + CONTEXT_LINES,
193
+ begin_line + MAX_LINES + CONTEXT_LINES,
194
+ source_buffer.line_count,
195
+ ].min
196
+ source_buffer.source.lines[(first_line - 1)..(last_line - 1)].join("")
197
+ end
198
+
199
+ private
200
+
201
+ attr_reader :location, :source_buffer
202
+
203
+ def line(type)
204
+ if location["lines"]
205
+ location["lines"][type]
206
+ elsif location["positions"]
207
+ position_to_line(location["positions"][type])
208
+ end
209
+ end
210
+
211
+ def position_to_line(position)
212
+ if position["line"]
213
+ position["line"]
214
+ else
215
+ @source_buffer.decompose_position(position["offset"]).first
216
+ end
217
+ end
218
+ end
219
+
220
+ class SourceFile
221
+ def initialize(path, filesystem)
222
+ @path = path
223
+ @filesystem = filesystem
224
+ end
225
+
226
+ attr_reader :path
227
+
228
+ def syntaxes
229
+ ext = File.basename(path).split(".").last.downcase
230
+ Array(LANGUAGES[ext])
231
+ end
232
+
233
+ def code
234
+ filesystem.read_path(path)
235
+ end
236
+
237
+ def buffer
238
+ @buffer ||= SourceBuffer.new(path, code)
239
+ end
240
+
241
+ def location(loc)
242
+ Location.new(buffer, loc)
243
+ end
244
+
245
+ private
246
+
247
+ attr_reader :filesystem
248
+ end
249
+
250
+ class Issue
251
+ MARKDOWN_CONFIG = { autolink: true, fenced_code_blocks: true, no_intra_emphasis: true, tables: true }.freeze
252
+
253
+ def initialize(data, filesystem)
254
+ @data = data
255
+ @filesystem = filesystem
256
+ end
257
+
258
+ def description
259
+ data["description"]
260
+ end
261
+
262
+ def body
263
+ @body ||=
264
+ begin
265
+ text = data.fetch("content", {}).fetch("body", "").strip
266
+ unless text.empty?
267
+ markdown(text)
268
+ end
269
+ end
270
+ end
271
+
272
+ def source
273
+ @source ||= SourceFile.new(
274
+ data.fetch("location", {}).fetch("path", ""),
275
+ filesystem,
276
+ )
277
+ end
278
+
279
+ def location
280
+ @location ||=
281
+ Location.new(
282
+ source.buffer,
283
+ data["location"],
284
+ )
285
+ end
286
+
287
+ def other_locations
288
+ @other_locations ||=
289
+ begin
290
+ data.fetch("other_locations", []).map do |loc|
291
+ [SourceFile.new(loc["path"], filesystem), loc]
292
+ end.to_h
293
+ end
294
+ end
295
+
296
+ def categories
297
+ data.fetch("categories", [])
298
+ end
299
+
300
+ def engine_name
301
+ data["engine_name"]
302
+ end
303
+
304
+ private
305
+
306
+ attr_reader :data, :filesystem
307
+
308
+ def markdown(text)
309
+ html = Redcarpet::Render::HTML.new(
310
+ escape_html: false,
311
+ link_attributes: { target: "_blank" },
312
+ )
313
+ redcarpet = Redcarpet::Markdown.new(html, MARKDOWN_CONFIG)
314
+ redcarpet.render(text)
315
+ end
316
+ end
317
+
318
+ class IssueCollection
319
+ def initialize(filesystem)
320
+ @collection = []
321
+ @filesystem = filesystem
322
+ end
323
+
324
+ def each(&block)
325
+ collection.each(&block)
326
+ end
327
+
328
+ def <<(issue)
329
+ if issue.is_a? Hash
330
+ issue = Issue.new(issue, filesystem)
331
+ end
332
+ collection.push(issue)
333
+ end
334
+
335
+ def any?
336
+ collection.any?
337
+ end
338
+
339
+ def syntaxes
340
+ collection.flat_map do |issue|
341
+ issue.source.syntaxes
342
+ end.uniq.sort
343
+ end
344
+
345
+ def categories
346
+ collection.flat_map(&:categories).uniq.sort
347
+ end
348
+
349
+ def engines
350
+ collection.map(&:engine_name).uniq.compact.sort
351
+ end
352
+
353
+ private
354
+
355
+ attr_reader :collection, :filesystem
356
+ end
357
+
358
+ class ReportTemplate
359
+ include ERB::Util
360
+ attr_reader :issues
361
+
362
+ TEMPLATE_PATH = File.expand_path(File.join(File.dirname(__FILE__), "templates/html.erb"))
363
+
364
+ def initialize(issues, filesystem)
365
+ @issues = issues
366
+ @filesystem = filesystem
367
+ end
368
+
369
+ def render
370
+ template = File.read(TEMPLATE_PATH)
371
+ ERB.new(template, nil, "-").result(binding)
372
+ end
373
+
374
+ def project_name
375
+ File.basename(filesystem.root)
376
+ end
377
+
378
+ def param(str)
379
+ str.downcase.gsub(/\s+/, "-")
380
+ end
381
+
382
+ def params(values)
383
+ values.map { |c| param c }.join(" ")
384
+ end
385
+
386
+ private
387
+
388
+ attr_reader :filesystem
389
+ end
390
+
391
+ def finished
392
+ puts ReportTemplate.new(issues, @filesystem).render
393
+ end
394
+
395
+ def failed(_)
396
+ exit 1
397
+ end
398
+
399
+ private
400
+
401
+ def issues
402
+ @issues ||= IssueCollection.new(@filesystem)
403
+ end
404
+
405
+ def warnings
406
+ @warnings ||= []
407
+ end
408
+
409
+ def measurements
410
+ @measurements ||= []
411
+ end
412
+ end
413
+ end
414
+ end
415
+ end
@@ -0,0 +1,38 @@
1
+ module CC
2
+ module Analyzer
3
+ module Formatters
4
+ class JSONFormatter < Formatter
5
+ def initialize(filesystem)
6
+ @filesystem = filesystem
7
+ @has_begun = false
8
+ end
9
+
10
+ def started
11
+ print "["
12
+ end
13
+
14
+ def finished
15
+ print "]\n"
16
+ end
17
+
18
+ def write(data)
19
+ document = JSON.parse(data)
20
+ document["engine_name"] = current_engine.name
21
+
22
+ if @has_begun
23
+ print ",\n"
24
+ end
25
+
26
+ print document.to_json
27
+ @has_begun = true
28
+ end
29
+
30
+ def failed(output)
31
+ $stderr.puts "\nAnalysis failed with the following output:"
32
+ $stderr.puts output
33
+ exit 1
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,101 @@
1
+ require "rainbow"
2
+ require "tty/spinner"
3
+ require "active_support/number_helper"
4
+
5
+ module CC
6
+ module Analyzer
7
+ module Formatters
8
+ class PlainTextFormatter < Formatter
9
+ def started
10
+ puts colorize("Starting analysis", :green)
11
+ end
12
+
13
+ def finished
14
+ puts
15
+
16
+ issues_by_path.each do |path, file_issues|
17
+ puts colorize("== #{path} (#{pluralize(file_issues.size, "issue")}) ==", :yellow)
18
+
19
+ IssueSorter.new(file_issues).by_location.each do |issue|
20
+ if (location = issue["location"])
21
+ source_buffer = @filesystem.source_buffer_for(location["path"])
22
+ print(colorize(LocationDescription.new(source_buffer, location, ": "), :cyan))
23
+ end
24
+
25
+ print(issue["description"])
26
+ print(colorize(" [#{issue["engine_name"]}]", "#333333"))
27
+ puts
28
+ end
29
+ puts
30
+ end
31
+
32
+ print(colorize("Analysis complete! Found #{pluralize(issues.size, "issue")}", :green))
33
+ unless warnings.empty?
34
+ print(colorize(" and #{pluralize(warnings.size, "warning")}", :green))
35
+ end
36
+ puts(colorize(".", :green))
37
+ end
38
+
39
+ def engine_running(engine, &block)
40
+ super(engine) do
41
+ result = with_spinner("Running #{current_engine.name}: ", &block)
42
+ if result.skipped?
43
+ puts(colorize("Skipped #{current_engine.name}: #{result.stderr}", :yellow))
44
+ end
45
+ end
46
+ end
47
+
48
+ def failed(output)
49
+ spinner.stop("Failed")
50
+ puts colorize("\nAnalysis failed with the following output:", :red)
51
+ puts output
52
+ exit 1
53
+ end
54
+
55
+ private
56
+
57
+ def spinner(text = nil)
58
+ @spinner ||= Spinner.new(text)
59
+ end
60
+
61
+ def with_spinner(text)
62
+ spinner(text).start
63
+ yield
64
+ ensure
65
+ spinner.stop
66
+ @spinner = nil
67
+ end
68
+
69
+ def colorize(string, *args)
70
+ rainbow.wrap(string).color(*args)
71
+ end
72
+
73
+ def rainbow
74
+ @rainbow ||= Rainbow.new.tap do |rainbow|
75
+ rainbow.enabled = false unless @output.tty?
76
+ end
77
+ end
78
+
79
+ def issues
80
+ @issues ||= []
81
+ end
82
+
83
+ def issues_by_path
84
+ issues.group_by { |i| i["location"]["path"] }.sort
85
+ end
86
+
87
+ def warnings
88
+ @warnings ||= []
89
+ end
90
+
91
+ def measurements
92
+ @measurements ||= []
93
+ end
94
+
95
+ def pluralize(number, noun)
96
+ "#{ActiveSupport::NumberHelper.number_to_delimited(number)} #{noun.pluralize(number)}"
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,35 @@
1
+ module CC
2
+ module Analyzer
3
+ module Formatters
4
+ class Spinner
5
+ def initialize(text)
6
+ @spinner = TTY::Spinner.new(text)
7
+ end
8
+
9
+ def start
10
+ return unless $stdout.tty? && !CLI.debug?
11
+ @thread = Thread.new do
12
+ loop do
13
+ @spinning = true
14
+ spinner.spin
15
+ sleep 0.075
16
+ end
17
+ end
18
+ end
19
+
20
+ def stop(text = "Done!")
21
+ if @spinning
22
+ spinner.stop(text)
23
+ print("\n")
24
+ @thread.kill
25
+ end
26
+ @spinning = false
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :spinner
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module CC
2
+ module Analyzer
3
+ module Formatters
4
+ autoload :Formatter, "cc/analyzer/formatters/formatter"
5
+ autoload :HTMLFormatter, "cc/analyzer/formatters/html_formatter"
6
+ autoload :JSONFormatter, "cc/analyzer/formatters/json_formatter"
7
+ autoload :PlainTextFormatter, "cc/analyzer/formatters/plain_text_formatter"
8
+ autoload :Spinner, "cc/analyzer/formatters/spinner"
9
+
10
+ FORMATTERS = {
11
+ html: HTMLFormatter,
12
+ json: JSONFormatter,
13
+ text: PlainTextFormatter,
14
+ }.freeze
15
+
16
+ def self.resolve(name)
17
+ FORMATTERS[name.to_sym] or raise Formatter::InvalidFormatterError, "'#{name}' is not a valid formatter. Valid options are: #{FORMATTERS.keys.join(", ")}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,69 @@
1
+ module CC
2
+ module Analyzer
3
+ class Issue
4
+ DEFAULT_SEVERITY = IssueValidations::SeverityValidation::MINOR
5
+ DEPRECATED_SEVERITY = IssueValidations::SeverityValidation::NORMAL
6
+
7
+ SPEC_ISSUE_ATTRIBUTES = %w[
8
+ categories
9
+ check_name
10
+ content
11
+ description
12
+ location
13
+ other_locations
14
+ remediation_points
15
+ severity
16
+ type
17
+ ]
18
+
19
+ def initialize(engine_name, output)
20
+ @engine_name = engine_name
21
+ @output = output
22
+ end
23
+
24
+ def as_json(*)
25
+ parsed_output.reverse_merge!(
26
+ "engine_name" => engine_name,
27
+ "fingerprint" => fingerprint,
28
+ ).merge!("severity" => severity)
29
+ end
30
+
31
+ def fingerprint
32
+ parsed_output.fetch("fingerprint") { default_fingerprint }
33
+ end
34
+
35
+ # Allow access to hash keys as methods
36
+ SPEC_ISSUE_ATTRIBUTES.each do |key|
37
+ define_method(key) do
38
+ parsed_output[key]
39
+ end
40
+ end
41
+
42
+ def path
43
+ parsed_output.fetch("location", {}).fetch("path", "")
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :engine_name, :output
49
+
50
+ def default_fingerprint
51
+ SourceFingerprint.new(self).compute
52
+ end
53
+
54
+ def severity
55
+ severity = parsed_output.fetch("severity", DEFAULT_SEVERITY)
56
+
57
+ if severity == DEPRECATED_SEVERITY
58
+ DEFAULT_SEVERITY
59
+ else
60
+ severity
61
+ end
62
+ end
63
+
64
+ def parsed_output
65
+ @parsed_output ||= JSON.parse(output)
66
+ end
67
+ end
68
+ end
69
+ end