7
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
|
# File 'lib/pwn/reports/html.rb', line 7
public_class_method def self.generate(opts = {})
out = PWN::Reports.resolve_path(opts.merge(ext: 'html'))
payload = PWN::Reports.package_evidence(payload: PWN::Reports.report_payload(opts), path: out)
rows = Array(payload[:priorities]).each_with_index.map do |priority, index|
"<tr><td>#{index + 1}</td><td>#{h(text: priority[:title])}</td><td>#{h(text: priority[:combined_severity])}</td><td>#{h(text: priority[:kind])}</td><td>#{h(text: priority[:rationale])}</td></tr>"
end
details = payload[:findings].map do |row|
fields = row.except('reproduction_steps', 'severity_justification', 'poc', 'evidence_artifacts', 'poc_export')
.map { |key, value| "<dt>#{h(text: key)}</dt><dd>#{h(text: value)}</dd>" }.join
steps = Array(row['reproduction_steps']).map { |step| "<li>#{h(text: step)}</li>" }.join
steps = steps.empty? ? '<p>Not supplied</p>' : "<ol>#{steps}</ol>"
justification = row['severity_justification'].to_s
code = PWN::Reports.poc_preview(text: row['poc'])
evidence = (Array(row['evidence_artifacts']) + [row['poc_export']].compact).map do |artifact|
link = "<a download href=\"#{h(text: artifact['attachment'])}\">#{h(text: artifact['label'])}</a>"
image = artifact['inline_image'] ? "<img src=\"#{h(text: artifact['attachment'])}\" alt=\"#{h(text: artifact['label'])}\">" : ''
"<li>#{link}#{image}<p>Kind: #{h(text: artifact['kind'])}; Handle: #{h(text: artifact['handle'])}; SHA-256: #{h(text: artifact['sha256'])}; Size: #{h(text: artifact['size'])} bytes</p></li>"
end.join
"<section><h2>#{h(text: row['title'])}</h2><dl>#{fields}</dl><h3>Reproduction steps</h3>#{steps}<h3>Severity justification</h3><p>#{h(text: justification.empty? ? 'Not supplied' : justification)}</p><h3>PoC command/code</h3><pre><code>#{h(text: code.empty? ? 'Not supplied' : code)}</code></pre><h3>Evidence</h3><ul>#{evidence}</ul></section>"
end.join
chains = payload[:attack_chains].map do |chain|
steps = Array(chain[:reproduction_steps]).map { |step| "<li>#{h(text: step)}</li>" }.join
links = Array(chain[:links]).map { |link| "<li>#{h(text: link[:from])} -> #{h(text: link[:to])}</li>" }.join
evidence = Array(chain[:evidence_artifacts]).map do |artifact|
artifact = artifact.transform_keys(&:to_s)
label = h(text: artifact['label'])
label = "<a download href=\"#{h(text: artifact['attachment'])}\">#{label}</a>" if artifact['attachment']
"<li>#{label}; Kind: #{h(text: artifact['kind'])}; Handle: #{h(text: artifact['handle'])}; SHA-256: #{h(text: artifact['sha256'])}; Size: #{h(text: artifact['size'])} bytes</li>"
end.join
"<section><h3>#{h(text: chain[:title])}</h3><p>Directed path: #{h(text: chain[:finding_ids].join(' -> '))}</p><p>Combined severity: #{h(text: chain[:combined_severity])}; Assessment: #{h(text: chain[:assessment_status])}</p><p>#{h(text: chain[:rationale])}</p><h4>Directed links</h4><ul>#{links}</ul><h4>PoC reproduction steps</h4><ol>#{steps}</ol><h4>Evidence</h4><ul>#{evidence}</ul></section>"
end.join
body = <<~HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>#{h(text: payload[:title])}</title>
</head>
<body>
<h1>#{h(text: payload[:title])}</h1>
#{summary_html(text: payload[:executive_summary])}
<h2>Ranked priorities</h2>
<table>
<thead>
<tr><th>rank</th><th>title</th><th>severity</th><th>kind</th><th>rationale</th></tr>
</thead>
<tbody>
#{rows.join("\n")}
</tbody>
</table>
<h2>Attack chains</h2>#{chains}
<h2>Finding technical details</h2>#{details}
</body>
</html>
HTML
File.write(out, body)
out
end
|