gitlab-ce/lib/gitlab/pdf/security/group_vulnerabilities_histo...

152 lines
6.3 KiB
Ruby

# frozen_string_literal: true
require "prawn"
require "prawn-svg"
module Gitlab
module PDF
module Security
class GroupVulnerabilitiesHistory
include Prawn::View
# rubocop:disable Layout/LineLength -- data strings
SEVERITY_ICONS = {
critical: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#dd2b0e" d="M6.706.185l4.088 2.31c.437.246.706.702.706 1.195v4.62c0 .493-.269.949-.706 1.195l-4.088 2.31a1.438 1.438 0 0 1-1.412 0l-4.088-2.31A1.376 1.376 0 0 1 .5 8.31V3.69c0-.493.269-.949.706-1.195L5.294.185a1.438 1.438 0 0 1 1.412 0z"/></svg>',
high: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#dd2b0e" d="M6.713.295l4.992 4.992a1.008 1.008 0 0 1 0 1.426l-4.992 4.992a1.008 1.008 0 0 1-1.426 0L.295 6.713a1.008 1.008 0 0 1 0-1.426L5.287.295a1.008 1.008 0 0 1 1.426 0z"/></svg>',
medium: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#fc9403" d="M5.04 10.454L.24 3.182c-.398-.603-.29-1.457.24-1.91C.688 1.097.94 1 1.2 1h9.6c.663 0 1.2.61 1.2 1.364 0 .295-.084.582-.24.818l-4.8 7.272c-.398.603-1.15.725-1.68.273a1.295 1.295 0 0 1-.24-.273z"/></svg>',
low: '<svg width="14" height="14" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><path d="m6.85,12.6a6.05,5.8 0 1 1 0,-11.6a6.05,5.8 0 0 1 0,11.6z" fill="#fc9403" id="severity_low"/></svg>',
info: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#428fdc" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm.75-12.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0zM8 5.25a.75.75 0 0 0-.75.75v4.5a.75.75 0 1 0 1.5 0V6A.75.75 0 0 0 8 5.25z"/></svg>',
unknown: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill="#868686" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zM4.927 4.99c-.285.429-.427.853-.427 1.27 0 .203.09.392.27.566.18.174.4.26.661.26.443 0 .744-.248.903-.746.168-.475.373-.835.616-1.08.243-.244.62-.366 1.134-.366.439 0 .797.12 1.075.363.277.242.416.54.416.892a.97.97 0 0 1-.136.502 1.91 1.91 0 0 1-.336.419 14.35 14.35 0 0 1-.648.558c-.34.282-.611.525-.812.73-.2.205-.362.443-.483.713-.322 1.245 1.35 1.345 1.736.456.047-.086.118-.18.213-.284.096-.103.223-.223.382-.36a41.14 41.14 0 0 0 1.194-1.034c.221-.204.412-.448.573-.73a1.95 1.95 0 0 0 .242-.984c0-.475-.141-.915-.424-1.32-.282-.406-.682-.726-1.2-.962-.518-.235-1.115-.353-1.792-.353-.728 0-1.365.14-1.911.423-.546.282-.961.637-1.246 1.066zm2.14 7.08a1 1 0 1 0 2 0 1 1 0 0 0-2 0z"/></svg>'
}.freeze
# rubocop:enable Layout/LineLength
def self.render(pdf, data: {})
new(pdf, data).render
end
def initialize(pdf, data)
@pdf = pdf
@charts = process_raw(data)
@selected_day_range = data&.dig(:selected_day_range)
@date_info = data&.dig(:date_info)
@width = 500
@height = 400
@icon_size = 14
@y = pdf.cursor
end
def render
return :noop if @charts.blank?
@pdf.bounding_box([0, @y], width: @pdf.bounds.right, height: @height) do
# Background
@pdf.save_graphics_state
@pdf.fill_color "F9F9F9"
@pdf.fill_rectangle [0, @pdf.bounds.top], @pdf.bounds.right, @height
@pdf.restore_graphics_state
# Title
@pdf.text_box(
s_('Vulnerability History'),
at: [0, @pdf.bounds.top - 10],
width: @pdf.bounds.right,
align: :center,
style: :bold,
size: 16
)
# Date information
@pdf.text_box(
"#{@date_info} (#{@selected_day_range} #{s_('Days')})",
at: [0, @pdf.bounds.top - 40],
width: @pdf.bounds.right,
align: :center,
size: 12
)
# the info table
draw_table_header
@charts.each_with_index do |chart, index|
draw_chart_row(chart, index)
end
end
end
private
def draw_table_header
@pdf.save_graphics_state
y_position = @pdf.bounds.top - 80
@pdf.bounding_box([10, y_position - 5], width: 100, height: 20) do
@pdf.text "Severity", style: :bold, size: 12
end
@pdf.bounding_box([@pdf.bounds.right - 150, y_position - 5], width: 50, height: 20) do
@pdf.text "%", style: :bold, size: 12, align: :center
end
@pdf.bounding_box([@pdf.bounds.right - 80, y_position - 5], width: 50, height: 20) do
@pdf.text "#", style: :bold, size: 12, align: :center
end
@pdf.stroke_color "000000"
@pdf.stroke_horizontal_line 0, @pdf.bounds.right, at: y_position - 25
@chart_start_position = y_position - 35
@pdf.restore_graphics_state
end
def draw_chart_row(chart, index)
row_height = 70
y_position = @chart_start_position - (index * row_height)
icon_svg = SEVERITY_ICONS[chart[:severity].to_sym]
@pdf.svg(icon_svg, at: [10, @pdf.cursor - 25], width: @icon_size, height: @icon_size)
@pdf.text_box(
chart[:severity].capitalize,
at: [30, @pdf.cursor + 12],
width: 100
)
svg_width = @pdf.bounds.right * 0.5
@pdf.svg chart[:svg], at: [100, y_position - 15], width: svg_width, height: 70
@pdf.text_box(
chart[:change_in_percent],
at: [@pdf.bounds.right - 150, y_position - 15],
width: 50,
height: 20,
align: :center,
style: :bold,
size: 12
)
@pdf.text_box(
chart[:current_count].to_s,
at: [@pdf.bounds.right - 80, y_position - 15],
width: 50,
height: 20,
align: :center,
style: :bold,
size: 12
)
end
def process_raw(data)
charts = data.present? ? data[:charts] : []
return if charts.blank?
charts.each do |chart|
svg = CGI.unescape(chart[:svg]).delete!("\n")[%r{(<svg.*</svg>)}, 1]
chart[:svg] = svg
end
charts
end
end
end
end
end