-
-
Notifications
You must be signed in to change notification settings - Fork 894
/
Copy pathmarkdown.go
256 lines (214 loc) · 7.46 KB
/
markdown.go
1
2
3
4
5
6
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
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package core
import (
"path"
"fmt"
"os"
"regexp"
"strings"
"github.com/fatih/color"
"github.com/j3ssie/osmedeus/libs"
"github.com/j3ssie/osmedeus/utils"
"github.com/spf13/cast"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
)
func (r *Runner) GenMarkdownReport(markdownFile string, outputHTML string) {
utils.DebugF("Reading markdown report from: %v", markdownFile)
// get the markdown template content
mdContent := utils.GetFileContent(markdownFile)
mdContent = ResolveData(mdContent, r.Target)
// replace all the <scanInfo /> tag
mdContent = r.ResolveScanInfoTag(mdContent)
// utils.DebugF("ResolveScanInfoTag:\n%v", mdContent)
// replace all the <reports /> tag
mdContent = r.ResolveReportsTag(mdContent)
// replace all the <content /> tag
mdContent = r.ResolveContentTag(mdContent)
// fmt.Println("mdContent", mdContent)
// generating the markdown file first
outputMD := strings.Replace(outputHTML, ".html", ".md", -1)
utils.WriteToFile(outputMD, mdContent)
utils.InforF("Generate markdown report: %v", outputMD)
utils.InforF("Generate HTML report: %v", outputHTML)
// finally convert to HTML
MarkDownToHTML(r.Opt, r.Input, outputMD, outputHTML)
}
func (r *Runner) ResolveScanInfoTag(rawMarkdown string) string {
re := regexp.MustCompile(`<scanInfo\s*/>`)
match := re.FindString(rawMarkdown)
if len(match) > 1 {
utils.DebugF("Replace scanInfo tag: %v", match)
scanInfo := fmt.Sprintf(`
| <!-- --> | <!-- --> |
|----------------|-------------|
| Target | **:target** |
| Running Time | **:runningTime** |
| Workflow | **:workflow** |
| Status | **:status** |
| Statistics | :statistics |
`)
status := "done"
if r.ScanObj.IsRunning {
status = "running"
}
statistics := fmt.Sprintf("`assets/%v`, `dns/%v`, `vulnerability/%v`, ", r.TargetObj.TotalAssets, r.TargetObj.TotalDns, r.TargetObj.TotalVulnerability)
replacements := map[string]string{
":target": r.ScanObj.InputName,
":runningTime": cast.ToString(int(r.RunningTime)/3600) + " hours",
":workflow": r.ScanObj.TaskName,
":statistics": statistics,
":status": status,
}
// generate the statistics info
for oldStr, newStr := range replacements {
scanInfo = strings.ReplaceAll(scanInfo, oldStr, newStr)
}
return strings.Replace(rawMarkdown, match, scanInfo, -1)
}
utils.DebugF("No scanInfo tag found")
return rawMarkdown
}
func (r *Runner) ResolveContentTag(rawData string) string {
finalMarkdown := rawData
// finding all the content tags and replace it with the content
re := regexp.MustCompile(`<content[^>]*>`)
matchs := re.FindAllString(rawData, -1)
for _, contentTag := range matchs {
utils.DebugF("Replace content tag: %v", color.GreenString(contentTag))
content := r.ResolveContentSrc(contentTag)
finalMarkdown = strings.Replace(finalMarkdown, contentTag, content, -1)
}
return finalMarkdown
}
func (r *Runner) ResolveContentSrc(tag string) string {
re := regexp.MustCompile(`src=\"(\S+)\"`)
match := re.FindStringSubmatch(tag)
if len(match) > 1 {
fileContent := utils.GetFileContent(match[1])
utils.DebugF("Replace content src: %v", color.GreenString(match[1]))
if strings.Contains(tag, "expand=true") {
return "```\n" + fileContent + "```"
}
if strings.Contains(tag, "shorten=true") || len(fileContent) > r.Opt.MDCodeBlockLimit {
return extendTag(fileContent)
}
return "```\n" + fileContent + "```"
}
return ""
}
func extendTag(str string) string {
data := "<details>\n<summary>Click to Expand</summary>\n\n" + "<pre>\n" + str + "\n</pre>" + "\n</details>"
return data
}
func (r *Runner) ResolveReportsTag(rawMarkDown string) string {
// rawData := utils.GetFileContent(markdownFile)
finalMarkdown := rawMarkDown
// finding all the reports tags
re := regexp.MustCompile(`<reports\s*/>`)
matchs := re.FindAllString(rawMarkDown, -1)
if len(matchs) == 0 {
utils.DebugF("No reports tag found")
return rawMarkDown
}
for _, reportTag := range matchs {
utils.DebugF("Replace content tag: %v", reportTag)
mdContent := ""
for _, report := range r.TargetObj.Reports {
// add the link if report file is HTML file
if report.ReportType == "html" {
mdContent += fmt.Sprintf("### %s -- [%s](%s) \n\n", report.Module, report.ReportName, report.ReportPath)
mdContent += "\n***\n"
continue
}
// add the full content if report file is a text file
mdContent += fmt.Sprintf("### %s -- *%s* \n\n", report.Module, report.ReportName)
fileContent := utils.GetFileContent(report.ReportPath)
if len(fileContent) > r.Opt.MDCodeBlockLimit {
mdContent += extendTag(fileContent)
} else {
mdContent += "```\n"
mdContent += fileContent
mdContent += "\n```\n"
}
mdContent += "\n***\n\n"
}
finalMarkdown = strings.Replace(finalMarkdown, reportTag, mdContent, -1)
}
return finalMarkdown
}
func MarkDownToHTML(options libs.Options, target string, markdownFile string, outputFile string) error {
css := path.Join(options.Env.DataFolder, "markdown/style.css")
var input []byte
var err error
if input, err = os.ReadFile(markdownFile); err != nil {
utils.ErrorF("Error reading %s: %v", markdownFile, err)
return err
}
// set up options
var extensions = parser.NoIntraEmphasis |
parser.Tables |
parser.FencedCode |
parser.Autolink |
parser.Strikethrough |
parser.SpaceHeadings
var renderer markdown.Renderer
// render the data into HTML
var htmlFlags html.Flags
htmlFlags |= html.Smartypants
htmlFlags |= html.UseXHTML
htmlFlags |= html.CompletePage
htmlFlags |= html.SmartypantsLatexDashes
htmlFlags |= html.SmartypantsFractions
params := html.RendererOptions{
Flags: htmlFlags,
CSS: css,
}
renderer = html.NewRenderer(params)
// parse and render
var output []byte
parser := parser.NewWithExtensions(extensions)
// @NOTE: beware of XSS as I assume you will trust the markdown content you generate
output = markdown.ToHTML(input, parser, renderer)
// html := bluemonday.UGCPolicy().SanitizeBytes(output) // skip the sanitization as we prefer more beautiful output
cssContent, err := os.ReadFile(css)
if err != nil {
return err
}
finalHTML := fmt.Sprintf(`
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/svg+xml" href="https://www.osmedeus.org/favicon.png">
<meta name="generator" content="Generated by https://github.com/gomarkdown/markdown">
<meta name="description" content="A Workflow Engine for Offensive Security">
<title>Osmedeus Executive Summary - %s</title>
<meta property="og:title" content="Osmedeus Next Generation">
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.osmedeus.org">
<meta property="og:image" content="https://raw.githubusercontent.com/osmedeus/assets/main/banner.png">
<meta property="og:description" content="A Workflow Engine for Offensive Security">
<meta name="twitter:card" content="">
<meta name="twitter:site" content="@OsmedeusEngine">
<meta name="twitter:creator" content="@OsmedeusEngine">
<style>%s</style>
</head>
<body>
%s
</body>
</html>
`, target, string(cssContent), string(output))
// output the result
var out *os.File
if out, err = os.Create(outputFile); err != nil {
utils.ErrorF("Error creating %s: %v", outputFile, err)
return err
}
defer out.Close()
if _, err = out.WriteString(finalHTML); err != nil {
utils.ErrorF("Error writing output: %v", err)
return err
}
return nil
}