Module: DeadFinder::Visualizer

Defined in:
lib/deadfinder/visualizer.rb

Overview

Visualizer module for generating images from scan results

Class Method Summary collapse

Class Method Details

.generate(data, output_path) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/deadfinder/visualizer.rb', line 8

def self.generate(data, output_path)
  # Extract summary data
  summary = data[:summary]
  return if summary.nil?

  total_tested = summary[:total_tested]
  return if total_tested.nil? || total_tested.zero?

  # Create a new image with transparent background
  png = ChunkyPNG::Image.new(500, 300, ChunkyPNG::Color::TRANSPARENT)

  # Draw stacked bar chart for status code distribution
  status_counts = data[:summary][:overall_status_counts] || {}
  bar_height = 70
  current_y = 110

  # Sort statuses by count descending
  sorted_statuses = status_counts.sort_by { |_, v| -v }

  sorted_statuses.each do |status, count|
    height = (count.to_f / total_tested * bar_height).to_i
    next if height.zero?

    color = case status.to_s
            when '200' then ChunkyPNG::Color.rgb(0, 255, 0) # Green for 200
            when /^3\d{2}$/ then ChunkyPNG::Color.rgb(255, 165, 0) # Orange for 3xx
            when /^4\d{2}$/ then ChunkyPNG::Color.rgb(255, 0, 0)   # Red for 4xx
            when /^5\d{2}$/ then ChunkyPNG::Color.rgb(128, 0, 128) # Purple for 5xx
            else ChunkyPNG::Color.rgb(128, 128, 128)               # Gray for others/error
            end

    (current_y..(current_y + height - 1)).each do |y|
      (20..480).each do |x|
        png[x, y] = color
      end
    end
    current_y += height
  end

  # Draw rounded outline around the bar area
  r = 10
  x1 = 10
  y1 = 100
  x2 = 490
  y2 = 190

  # Top line
  ((x1 + r)..(x2 - r)).each do |x|
    png[x, y1] = ChunkyPNG::Color.rgba(0, 0, 0, 128)
    # Bottom line
    png[x, y2] = ChunkyPNG::Color.rgba(0, 0, 0, 128)
  end
  # Left line
  ((y1 + r)..(y2 - r)).each do |y|
    png[x1, y] = ChunkyPNG::Color.rgba(0, 0, 0, 128)
    # Right line
    png[x2, y] = ChunkyPNG::Color.rgba(0, 0, 0, 128)
  end

  # Corners: quarter circles
  # Top-left
  (0..90).each do |angle|
    rad = angle * Math::PI / 180
    cx = x1 + r
    cy = y1 + r
    px = cx + (r * Math.cos(rad))
    py = cy + (r * Math.sin(rad))
    png[px.to_i, py.to_i] = ChunkyPNG::Color.rgba(0, 0, 0, 128) if px >= x1 && py >= y1
  end
  # Top-right
  (90..180).each do |angle|
    rad = angle * Math::PI / 180
    cx = x2 - r
    cy = y1 + r
    px = cx + (r * Math.cos(rad))
    py = cy + (r * Math.sin(rad))
    png[px.to_i, py.to_i] = ChunkyPNG::Color.rgba(0, 0, 0, 128) if px <= x2 && py >= y1
  end
  # Bottom-left
  (270..360).each do |angle|
    rad = angle * Math::PI / 180
    cx = x1 + r
    cy = y2 - r
    px = cx + (r * Math.cos(rad))
    py = cy + (r * Math.sin(rad))
    png[px.to_i, py.to_i] = ChunkyPNG::Color.rgba(0, 0, 0, 128) if px >= x1 && py <= y2
  end
  # Bottom-right
  (180..270).each do |angle|
    rad = angle * Math::PI / 180
    cx = x2 - r
    cy = y2 - r
    px = cx + (r * Math.cos(rad))
    py = cy + (r * Math.sin(rad))
    png[px.to_i, py.to_i] = ChunkyPNG::Color.rgba(0, 0, 0, 128) if px <= x2 && py <= y2
  end

  # Save the image
  png.save(output_path, :fast_rgba)
end