Class: Necropsy::Reporter

Inherits:
Object
  • Object
show all
Defined in:
lib/necropsy/reporter.rb

Constant Summary collapse

FORMATS =
%i[human json ndjson yaml yml sarif github annotations].freeze
DEFAULT_MIN_CONFIDENCE =
:medium
DEFINITION_RESOLUTION_SAMPLE_LIMIT =
5

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(report) ⇒ Reporter

Returns a new instance of Reporter.



9
10
11
# File 'lib/necropsy/reporter.rb', line 9

def initialize(report)
  @report = report
end

Class Method Details

.render_analysis_health(analysis_health) ⇒ Object



33
34
35
36
37
38
39
40
41
# File 'lib/necropsy/reporter.rb', line 33

def self.render_analysis_health(analysis_health)
  lines = ["Analysis health: #{analysis_health.status}"]
  analysis_health.reasons.each do |reason|
    location = [reason['file'], reason['line']].compact.join(':')
    suffix = location.empty? ? '' : " at #{location}"
    lines << "  [#{reason.fetch('severity')}] #{reason.fetch('code')}#{suffix}: #{reason['message']}"
  end
  lines.join("\n")
end

.render_baseline_review(review_report) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/necropsy/reporter.rb', line 13

def self.render_baseline_review(review_report)
  lines = [
    'Baseline migration requires review',
    "Baseline: #{review_report.fetch('baseline_path')}",
    "Schema: v#{review_report.fetch('baseline_schema_version')}",
    "Ambiguous mappings: #{review_report.fetch('ambiguities').length}"
  ]
  review_report.fetch('ambiguities').each do |ambiguity|
    baseline = ambiguity.fetch('baseline')
    label = baseline['definition_id'] || baseline['symbol_id'] || baseline['node_id'] || baseline['fingerprint']
    lines << "  #{label} via #{ambiguity.fetch('strategy')} (#{ambiguity.fetch('reason')})"
    ambiguity.fetch('candidates').each do |candidate|
      lines << "    #{candidate.fetch('symbol_id')} [#{candidate.fetch('definition_id')}] " \
               "#{candidate.fetch('file')}:#{candidate.fetch('line')}"
    end
  end
  lines << 'Regenerate the baseline after reviewing every ambiguous physical definition.'
  lines.join("\n")
end

Instance Method Details

#each_ndjson {|ndjson_record('report', report.to_h(include_graph: false))| ... } ⇒ Object

Yields:

  • (ndjson_record('report', report.to_h(include_graph: false)))


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/necropsy/reporter.rb', line 63

def each_ndjson
  return enum_for(__method__) unless block_given?

  yield ndjson_record('report', report.to_h(include_graph: false))
  graph = report.graph
  {
    'node' => graph.nodes.values,
    'call_site' => graph.call_sites,
    'edge' => graph.edges,
    'edge_relation' => graph.edge_relations,
    'evidence' => graph.evidence_records,
    'entry_point' => graph.entry_points,
    'class_info' => graph.class_infos.values,
    'profile' => graph.profiles,
    'resolution' => graph.resolution_records,
    'blocker' => graph.blockers,
    'source_error' => graph.source_errors
  }.each do |record_type, records|
    records.each { |record| yield ndjson_record(record_type, record.to_h) }
  end
  yield ndjson_record('graph_metadata', {
                        'edge_projection' => 'conservative',
                        'instantiated_classes' => graph.instantiated_classes.to_a.sort,
                        'file_statuses' => graph.file_statuses.transform_values(&:to_s),
                        'source_domains' => graph.source_domains.transform_values(&:to_s),
                        'scope_diagnostics' => graph.scope_diagnostics,
                        'observation' => graph.observation
                      })
end

#render(format: :human, min_confidence: DEFAULT_MIN_CONFIDENCE, include_graph: false) ⇒ Object

Raises:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/necropsy/reporter.rb', line 43

def render(format: :human, min_confidence: DEFAULT_MIN_CONFIDENCE, include_graph: false)
  normalized_format = format.to_sym
  raise Error, "Unknown report format: #{format}" unless FORMATS.include?(normalized_format)

  case normalized_format
  when :json
    report.to_json(include_graph: include_graph)
  when :ndjson
    each_ndjson.to_a.join("\n")
  when :sarif
    render_sarif(min_confidence)
  when :github, :annotations
    render_github_annotations(min_confidence)
  when :yaml, :yml
    report.to_yaml(include_graph: include_graph)
  when :human
    render_human(min_confidence)
  end
end