package output
import (
"html"
"html/template"
"os"
"time"
"github.com/ffuf/ffuf/v2/pkg/ffuf"
)
type htmlResult struct {
Input map[string]string
Position int
StatusCode int64
ContentLength int64
ContentWords int64
ContentLines int64
ContentType string
RedirectLocation string
ScraperData string
Duration time.Duration
ResultFile string
Url string
Host string
HTMLColor string
FfufHash string
}
type htmlFileOutput struct {
CommandLine string
Time string
Keys []string
Results []htmlResult
}
const (
htmlTemplate = `
FFUF Report -
{{ .CommandLine }}
{{ .Time }}
|result_raw|StatusCode{{ range $keyword := .Keys }}|{{ $keyword | printf "%s" }}{{ end }}|Url|RedirectLocation|Position|ContentLength|ContentWords|ContentLines|ContentType|Duration|Resultfile|ScraperData|FfufHash|
| Status |
{{ range .Keys }} {{ . }} | {{ end }}
URL |
Redirect location |
Position |
Length |
Words |
Lines |
Type |
Duration |
Resultfile |
Scraper data |
Ffuf Hash |
{{range $result := .Results}}
|result_raw|{{ $result.StatusCode }}{{ range $keyword, $value := $result.Input }}|{{ $value | printf "%s" }}{{ end }}|{{ $result.Url }}|{{ $result.RedirectLocation }}|{{ $result.Position }}|{{ $result.ContentLength }}|{{ $result.ContentWords }}|{{ $result.ContentLines }}|{{ $result.ContentType }}|{{ $result.Duration }}|{{ $result.ResultFile }}|{{ $result.ScraperData }}|{{ $result.FfufHash }}|
| {{ $result.StatusCode }} |
{{ range $keyword, $value := $result.Input }}
{{ $value | printf "%s" }} |
{{ end }}
{{ $result.Url }} |
{{ $result.RedirectLocation }} |
{{ $result.Position }} |
{{ $result.ContentLength }} |
{{ $result.ContentWords }} |
{{ $result.ContentLines }} |
{{ $result.ContentType }} |
{{ $result.Duration }} |
{{ $result.ResultFile }} |
{{ $result.ScraperData }} |
{{ $result.FfufHash }} |
{{ end }}
`
)
// colorizeResults returns a new slice with HTMLColor attribute
func colorizeResults(results []ffuf.Result) []ffuf.Result {
newResults := make([]ffuf.Result, 0)
for _, r := range results {
result := r
result.HTMLColor = "black"
s := result.StatusCode
if s >= 200 && s <= 299 {
result.HTMLColor = "#adea9e"
}
if s >= 300 && s <= 399 {
result.HTMLColor = "#bbbbe6"
}
if s >= 400 && s <= 499 {
result.HTMLColor = "#d2cb7e"
}
if s >= 500 && s <= 599 {
result.HTMLColor = "#de8dc1"
}
newResults = append(newResults, result)
}
return newResults
}
func writeHTML(filename string, config *ffuf.Config, results []ffuf.Result) error {
results = colorizeResults(results)
ti := time.Now()
keywords := make([]string, 0)
for _, inputprovider := range config.InputProviders {
keywords = append(keywords, inputprovider.Keyword)
}
htmlResults := make([]htmlResult, 0)
for _, r := range results {
ffufhash := ""
strinput := make(map[string]string)
for k, v := range r.Input {
if k == "FFUFHASH" {
ffufhash = string(v)
} else {
strinput[k] = string(v)
}
}
strscraper := ""
for k, v := range r.ScraperData {
if len(v) > 0 {
strscraper = strscraper + "" + html.EscapeString(k) + ":
"
firstval := true
for _, val := range v {
if !firstval {
strscraper += "
"
}
strscraper += html.EscapeString(val)
firstval = false
}
strscraper += "
"
}
}
hres := htmlResult{
Input: strinput,
Position: r.Position,
StatusCode: r.StatusCode,
ContentLength: r.ContentLength,
ContentWords: r.ContentWords,
ContentLines: r.ContentLines,
ContentType: r.ContentType,
RedirectLocation: r.RedirectLocation,
ScraperData: strscraper,
Duration: r.Duration,
ResultFile: r.ResultFile,
Url: r.Url,
Host: r.Host,
HTMLColor: r.HTMLColor,
FfufHash: ffufhash,
}
htmlResults = append(htmlResults, hres)
}
outHTML := htmlFileOutput{
CommandLine: config.CommandLine,
Time: ti.Format(time.RFC3339),
Results: htmlResults,
Keys: keywords,
}
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
templateName := "output.html"
t := template.New(templateName).Delims("{{", "}}")
_, err = t.Parse(htmlTemplate)
if err != nil {
return err
}
err = t.Execute(f, outHTML)
return err
}