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
65
66
67
|
# File 'lib/pwn/reports/markdown.rb', line 7
public_class_method def self.generate(opts = {})
out = PWN::Reports.resolve_path(opts.merge(ext: 'md'))
payload = PWN::Reports.package_evidence(payload: PWN::Reports.report_payload(opts), path: out)
lines = ["# #{escape(text: payload[:title])}", '']
lines += ['## Executive summary', '', escape(text: payload[:executive_summary]), ''] unless payload[:executive_summary].to_s.empty?
lines += ['## Ranked priorities', '']
Array(payload[:priorities]).each_with_index do |priority, index|
lines << "#{index + 1}. **#{escape(text: priority[:combined_severity])}** — #{escape(text: priority[:title])} (#{escape(text: priority[:kind])}). #{escape(text: priority[:rationale])}"
end
lines += ['', '## Attack chains', '']
payload[:attack_chains].each do |chain|
lines += ["### #{escape(text: chain[:title])}", '',
"Directed path: #{escape(text: chain[:finding_ids].join(' -> '))}", '',
"Combined severity: **#{escape(text: chain[:combined_severity])}**; Assessment: #{escape(text: chain[:assessment_status])}", '',
escape(text: chain[:rationale]), '', '#### Directed links', '']
Array(chain[:links]).each { |link| lines << "- #{escape(text: "#{link[:from]} -> #{link[:to]}")}" }
lines += ['', '#### PoC reproduction steps', '']
Array(chain[:reproduction_steps]).each_with_index { |step, index| lines << "#{index + 1}. #{escape(text: step)}" }
lines += ['', '#### Evidence', '']
Array(chain[:evidence_artifacts]).each do |artifact|
artifact = artifact.transform_keys(&:to_s)
label = escape(text: artifact['label'])
lines << (artifact['attachment'] ? "[#{label}](#{artifact['attachment']})" : label)
lines << "- Kind: #{escape(text: artifact['kind'])}; Handle: #{escape(text: artifact['handle'])}; SHA-256: #{escape(text: artifact['sha256'])}; Size: #{escape(text: artifact['size'])} bytes"
end
lines << ''
end
lines += ['## Findings', '']
if payload[:findings].empty?
lines << '_No findings._'
else
payload[:findings].each do |row|
lines << "### #{escape(text: row['id'].to_s.empty? ? row['title'] : "#{row['id']}: #{row['title']}")}"
lines << ''
row.each do |key, val|
next if %w[id title reproduction_steps severity_justification poc evidence_artifacts poc_export].include?(key.to_s)
lines << "- **#{escape(text: key)}**: #{escape(text: val)}"
end
lines += ['', '#### Reproduction steps', '']
steps = Array(row['reproduction_steps'])
lines += steps.empty? ? ['Not supplied'] : steps.each_with_index.map { |step, index| "#{index + 1}. #{escape(text: step)}" }
justification = row['severity_justification'].to_s
lines += ['', '#### Severity justification', '', justification.empty? ? 'Not supplied' : escape(text: justification)]
code = PWN::Reports.poc_preview(text: row['poc'])
fence = '`' * [3, (code.scan(/`+/).map(&:length).max || 0) + 1].max
lines += ['', '#### PoC command/code', '', fence, code.empty? ? 'Not supplied' : code, fence]
lines += ['', '#### Evidence', '']
(Array(row['evidence_artifacts']) + [row['poc_export']].compact).each do |artifact|
label = escape(text: artifact['label'])
link = "[#{label}](#{artifact['attachment']})"
lines << "#{'!' if artifact['inline_image']}#{link}"
lines << "- Kind: #{escape(text: artifact['kind'])}; Handle: #{escape(text: artifact['handle'])}; SHA-256: #{artifact['sha256']}; Size: #{artifact['size']} bytes"
end
lines << ''
end
end
File.write(out, "#{lines.join("\n").rstrip}\n")
out
end
|