Я работал над проектом, в котором мне нужно изменить платформу сканера безопасности веб-приложений Arachni и заставить ее генерировать и загружать отчет о сканировании в формате PDF. Само веб-приложение работает на Ruby на Rails. Вот ссылка на приложение, о котором я говорю.
https://www.arachni-scanner.com/
Теперь приложение по умолчанию генерирует пять форматов файлов: HTML (или HTML .zip), JSON, маршал, YAML и XML. Я пытаюсь включить PDF, и отчет в формате PDF должен выглядеть точно так же, как содержимое отчета HTML, диаграмм и всего остального. Папка приложения имеет свой собственный каталог библиотек. Существует одна библиотека под названием Arachni-1.5.1, и я пришел к выводу, что именно в ней создаются сгенерированные форматы файлов. Я могу привести один пример, а именно файл библиотеки, который предположительно создает отчет HTML .zip. Это код из библиотеки Arachni-1.5.1 в папке с именем reporters, и имя файла - html .rb.
`module TemplateUtilities
<code> def base64_encode( string )
Base64.encode64( string ).gsub( /\n/, '' )
end
def normalize( str )
str.to_s.recode
end
def md( markdown )
html = Kramdown::Document.new( markdown ).to_html.recode
Loofah.fragment( html ).scrub!(:prune).to_s
end
def key_to_words( k )
k.to_s.capitalize.gsub( '_', ' ' )
end
def code_highlight( code, language = :html, options = {} )
return if !code
lines = CodeRay.scan( code.recode, language ).
html( css: :style ).lines.to_a
if options[:from]
from = [0, options[:from]].max
else
from = 0
end
if options[:to]
to = [lines.size, options[:to]].min
else
to = lines.size - 1
end
code = '<div class="code-container"><table class="CodeRay"><tbody><tr><td class="line-numbers"><pre>'
from.upto(to) do |i|
if options[:anchor_id]
line = "<a href='#{id_to_location "#{options[:anchor_id]}-#{i}"}'>#{i}</a>"
else
line = "#{i}"
end
if options[:breakpoint] && options[:breakpoint] == i
code << "<span class='breakpoint'>#{line}</span>"
else
code << line
end
code << "\n"
end
code << '
'
from.upto(to) do |i|
line = "<span id='#{options[:anchor_id]}-#{i}'>#{lines[i]}</span>"
if options[:breakpoint] && options[:breakpoint] == i
code << "<span class='breakpoint'>#{line}</span>"
else
code << line.to_s
end
end
code + '
'end def highlight_proof (string, proof) proof = proof.to_s.recode string = string.to_s.recode return escape HTML (строка), если proof.to_s.empty? return escape HTML (строка), если! string.include? (подтверждение) escaped_proof = escape HTML (подтверждение) escaped_response_body = escape HTML (строка) escaped_response_body.gsub (escaped_proof, " # {escaped_proof} " ) end def data_dump (data) ap = AwesomePrint :: Inspector.new (plain: true, html: true) " # {ap.awesome (data)}" конец # Осторожно избегает HTML и преобразует в UTF-8 при удалении # недопустимые последовательности символов. def escape HTML (str) CGI.escape HTML (нормализовать (стр.)) конец def highlight_issue_page_body (проблема, span_class) вернуть escape HTML (issue.page.body), если! issue.page.body.include? (issue.proof) escaped_proof = escape HTML (проблема. защита) escaped_response_body = escape HTML (issue.page.body) escaped_response_body.gsub ( escaped_proof, " # {escaped_proof} " ) конец def issue_location (проблема) id_to_location (issue_id (проблема)) конец def Issue_id (проблема) Проблема = отчет.Исса_by_digest (проблема.digest) "questions - # {'un' if issue.untrusted?} trust-severity-" << "# {issue.severity} - {# issue.check [: короткое_имя]} - {# issue.digest}" конец def id_to_location (id) "#! / # {id.gsub ('-', '/')}" конец def erb (tpl, params = {}) scope = TemplateScope.new (params) tpl = tpl.to_s + '.erb' если tpl.is_a? (Символ) путь = File.exist? (tpl)? tpl: TEMPLATE_DIR + tpl ERB.new (IO.read (путь) .recode) .result (scope.get_binding) спасение ap tpl повышение конец конец включить TemplateUtilities Класс TemplateScope включить TemplateUtilities ISSUES_URL = '
https://github.com/Arachni/arachni/issues' def initialize (params = {}) обновить параметры обновить self.class.global_data конец обновление по умолчанию (параметры) params.each {| name, value | self [name] = значение} сам конец def [] = (имя, значение) self.class.send (: attr_accessor, name) instance_variable_set ("@ # {name.to_s}", значение) сам конец def prep_description (str) побег HTML Арачни :: Репортеры :: HTML .prep_description (str) конец def get_plugin_info (name) report.plugins [name.to_sym] конец def js_multiline (str) "\" "+ normalize (str) .gsub (" \ n ", '\ n') +" \ "" конец def get_binding переплет конец def self.global_data = (данные) @global_data = данные конец def self.global_data @global_data конец конец def global_data grouped_issues = {доверенный: {}, ненадежный: {}} Arachni :: Issue :: Severity :: ORDER.each do | серьезность | by_severity = report.issues.select {| i | i.severity.to_sym == серьезность} далее, если by_severity.empty? by_name = {} by_severity.each do | issue | by_name [Issue.Name] || = [] by_name [Issue.Name] << выпуск заканчивается следующим, если by_name.empty? grouped_issues [: доверенный] [by_severity.first.severity] = by_name.inject ({}) do | h, (имя, проблемы) | я = Issues.Slect (&: доверенный?) следующий ч, если я.Impty? h [name] = i h end grouped_issues [: недоверенный] [by_severity.first.severity] = by_name.inject ({}) do | h, (имя, проблемы) | я = Issues.select (&: Ненадежный?) следующий ч, если я.Impty? h [имя] = i h конец [: доверенный,: ненадежный] .each do | t | если grouped_issues [t] [by_severity.first.severity] .empty? grouped_issues [t] .delete by_severity.first.severity конец конец конец конец [: доверенный,: ненадежный] .each do | t | grouped_issues.delete (t) if grouped_issues [t] .empty? end prepare_data.merge (report: report, grouped_issues: grouped_issues, plugins: format_plugin_results) end # Запускает отчет HTML. def run FileUtils.rm_rf outfile print_line print_status 'Создание HTML отчета ...' TemplateScope.global_data = global_data tmpdir = "# {Arachni :: Options.paths.tmpdir} / # {generate_token} /" FileUtils.rm_rf tmpdir FileUtils. mkdir_p tmpdir FileUtils.mkdir_p "# {tmpdir} / js / lib" FileUtils.mkdir_p "# {tmpdir} / css / lib" FileUtils.cp_r "# {TEMPLATE_DIR} / fonts", "# {tmpdir} /" FileUtil. "# {TEMPLATE_DIR} / js / lib", "# {tmpdir} / js /" FileUtils.cp_r "# {TEMPLATE_DIR} / css / lib", "# {tmpdir} / css /"% w (js / helpers. js js / init. js .erb js / charts. js .erb js / configuration. js .erb css / main. css). Каждый выполняет | f | если f.end_with? '.erb' IO.write ("# {tmpdir} / # {f.split ('. erb'). first}", erb ("# {TEMPLATE_DIR} / # {f}")) иначе FileUtils.cp ( "# {TEMPLATE_DIR} / # {f}", "# {tmpdir} / # {f}") end end IO.write ("# {tmpdir} /index.html", erb (TEMPLATE_FILE)) compress ( tmpdir, outfile) FileUtils.rm_rf tmpdir print_status "Сохранено в '# {outfile}'." end def self.info {name: 'HTML', описание:% q {Экспортирует результаты аудита в виде сжатого отчета HTML.}, content_type: 'application / zip', автор: 'Tasos "Zapotek" Laskos ', версия:' 0.4.3 ', опции: [Options.outfile ('. html .zip '), Options.skip_responses]} конец частного сжатия сжатия (каталог, архив) Zip :: File.open (архив, Zip :: File :: CREATE) do | zipfile | Dir [File.join (каталог, '**', '**')]. Каждый do | файл | zipfile.add (file.sub (каталог, ''), файл) конец конец конец архив конец определение def self.prep_description (str) placeholder = '-' + rand (1000) .to_s + '-' cstr = str.gsub (/ ^ \ s * $ / xm, заполнитель) cstr.gsub! (/ ^ \ s * / xm, '') cstr.gsub! (заполнитель, "\ n") cstr.chomp end def prepare_data graph_data = {severities : {Severity :: HIGH.to_sym => 0, Severity :: MEDIUM.to_sym => 0, Severity :: LOW.to_sym => 0, Severity :: INFORMATIONAL.to_sym => 0}, severity_for_issue: {}, severity_index_for_issue: {}, severity_regions: {}, проблемы: {}, проблемы_кортовые имена: Set.new, доверенные_исследования: {}, недоверенные_исследования: {}, элементы: { Element :: Form.type => 0, Element :: Form :: DOM.type => 0, Element :: Link.type => 0, Element :: Link :: DOM.type => 0, Element :: Cook ie .type => 0, Element :: Cook ie :: DOM.type => 0, Element :: LinkTemplate.type => 0, Element :: LinkTemplate :: DOM.type => 0, Элемент: : Header.type => 0, Element :: Body.type => 0, Element :: Path.type => 0, Element :: Server.type => 0, Element :: GenericDOM.type => 0, Элемент: : JSON .type => 0, Element :: XML .type => 0, Element :: UIInput :: DOM.type => 0, Element :: UIForm :: DOM.type => 0}, проверка : {'Да' => 0, 'Нет' => 0}, доверие: {'Доверенные' => 0, 'Ненадежные' => 0}} total_severities = 0 total_elements = 0 has_trusted_issues = false has_untrusted_issues = false last_severity = nil report.issues.each.with_index do | issue, i | graph_data [: серьезность] [Issue.severity.to_sym] + = 1 total_severities + = 1 graph_data [: проблемы] [проблема.имя] || = 0 graph_data [: проблемы] [проблема.имя] + = 1 graph_data [: элементы ] [Issue.vector.class.type] + = 1 total_elements + = 1 проверка = Issue.Untrusted? ? «Да»: «Нет» graph_data [: проверка] [проверка] + = 1 graph_data [: недоверенные_северности] || = {} graph_data [: недоверенные_северности] [вопрос.severity.to_sym] || = 0 graph_data [: доверенные_северности] | | = {} graph_data [: trusted_severities] [Issue.severity.to_sym] || = 0 graph_data [:rust_issues] [Issue.Name] || = 0 graph_data [: untrusted_issues] [issue.name] || = 0 graph_data [ : Issues_shortnames] << Issue.check [: короткое имя] graph_data [: severity_for_issue] [Issue.check [: Shortname]] = Issue.severity.to_s new_region =! Graph_data [: Severity_regions] .include? (Issue.severity.to_sym)? graph_data [: severity_regions] [issue.severity.to_sym] || = {} graph_data [: severity_regions] [issue.severity.to_sym] [: class] = "серьезность - # {issue.severity.to_sym}" graph_data [: severity_regions ] [issue.severity.to_sym] [: start] || = graph_data [: проблемы] .size - 1, если new_region && last_severity graph_data [: severity_regions] [last_severity] [: конец] = graph_data [: проблемы] .size - 2 end last_severity = issue.severity.to_sym graph_data [: severity_ index_for_issue] [Issue.Name] = Issue :: Severity :: ORDER.reverse.index (issue.severity.to_sym) + 1, если Issue.Trusted? has_trusted_issues = true : trust] ['Untrusted'] + = 1 graph_data [: untrusted_severities] [Issue.severity.to_sym] + = 1 graph_data [: untrusted_issues] [issue.name] + = 1 конец end graph_data [: Issues_ShortNames] = Graph_Data [: Issues_Shortnames] Как я могу превратить что-то подобное в PDF? Я также должен отметить, что любое редактирование, которое я применяю в этой библиотеке, может привести к тому, что приложение будет обрабатывать sh каждый раз, когда я посещаю страницу сканирования. Веб-приложение также поставляется с библиотекой Kramdown-1.4. 1, который имеет своего рода конвертер PDF; но я честно не уверен, что это самый идеальный инструмент для использования. Я искал бесчисленные источники, и это не кажется многим известной проблемой. </p>