Feat screenshot html (#1226)

* create basic html indexing functionality

* change html style

* populate response info

* matched html response info with flags

* implement html template for screenshots

* remove unused func

* removing unused loop

* moving summary to static folder

* using file prefix on windows

* removing test output

---------

Co-authored-by: Mzack9999 <mzack9999@protonmail.com>
This commit is contained in:
Bugra Kocabay 2023-07-10 14:48:19 +03:00 committed by GitHub
parent f5c5fc63e1
commit 12db59a1d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 207 additions and 3 deletions

View File

@ -7,6 +7,7 @@ import (
"encoding/csv"
"encoding/json"
"fmt"
"html/template"
"io"
"net"
"net/http"
@ -28,8 +29,10 @@ import (
dsl "github.com/projectdiscovery/dsl"
"github.com/projectdiscovery/httpx/common/customextract"
"github.com/projectdiscovery/httpx/common/hashes/jarm"
"github.com/projectdiscovery/httpx/static"
"github.com/projectdiscovery/mapcidr/asn"
errorutil "github.com/projectdiscovery/utils/errors"
osutil "github.com/projectdiscovery/utils/os"
"github.com/Mzack9999/gcache"
"github.com/logrusorgru/aurora"
@ -603,12 +606,21 @@ func (r *Runner) RunEnumeration() {
}
// output routine
wgoutput := sizedwaitgroup.New(1)
wgoutput := sizedwaitgroup.New(2)
wgoutput.Add()
output := make(chan Result)
go func(output chan Result) {
nextStep := make(chan Result)
go func(output chan Result, nextSteps ...chan Result) {
defer wgoutput.Done()
defer func() {
for _, nextStep := range nextSteps {
close(nextStep)
}
}()
var f, indexFile, indexScreenshotFile *os.File
if r.options.Output != "" {
@ -860,8 +872,59 @@ func (r *Runner) RunEnumeration() {
//nolint:errcheck // this method needs a small refactor to reduce complexity
f.WriteString(row + "\n")
}
for _, nextStep := range nextSteps {
nextStep <- resp
}
}
}(output)
}(output, nextStep)
// HTML Summary
// - needs output of previous routine
// - separate goroutine due to incapability of go templates to render from file
wgoutput.Add()
go func(output chan Result) {
defer wgoutput.Done()
if r.options.Screenshot {
screenshotHtmlPath := filepath.Join(r.options.StoreResponseDir, "screenshot", "screenshot.html")
screenshotHtml, err := os.Create(screenshotHtmlPath)
if err != nil {
gologger.Warning().Msgf("Could not create HTML file %s\n", err)
}
defer screenshotHtml.Close()
templateMap := template.FuncMap{
"safeURL": func(u string) template.URL {
if osutil.IsWindows() {
u = fmt.Sprintf("file:///%s", u)
}
return template.URL(u)
},
}
tmpl, err := template.
New("screenshotTemplate").
Funcs(templateMap).
Parse(static.HtmlTemplate)
if err != nil {
gologger.Warning().Msgf("Could not create HTML template: %v\n", err)
}
if err = tmpl.Execute(screenshotHtml, struct {
Options Options
Output chan Result
}{
Options: *r.options,
Output: output,
}); err != nil {
gologger.Warning().Msgf("Could not execute HTML template: %v\n", err)
}
}
// fallthrough if anything is left in the buffer unblocks if screenshot is false
for range output {
}
}(nextStep)
wg := sizedwaitgroup.New(r.options.Threads)

133
static/html-summary.html Normal file
View File

@ -0,0 +1,133 @@
<!DOCTYPE html>
<html>
<head>
<title>Screenshot Table</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
}
table {
margin-top: 20px;
border-collapse: collapse;
}
th,
td {
padding: 10px;
text-align: center;
border: 1px solid black;
}
.thumbnail {
width: 400px;
height: 300px;
object-fit: cover;
}
</style>
</head>
<body>
<table style="margin: 20px auto; border-collapse: collapse">
<thead>
<tr>
<th style="padding: 10px; text-align: center; border: 1px solid black">
<strong>Response Info</strong>
</th>
<th style="padding: 10px; text-align: center; border: 1px solid black">
<strong>Screenshot</strong>
</th>
</tr>
</thead>
<tbody>
{{ $ExtractTitle := .Options.ExtractTitle }}
{{ $OutputStatusCode := .Options.StatusCode }}
{{ $OutputContentLength := .Options.ContentLength }}
{{ $Favicon := .Options.Favicon }}
{{ $OutputResponseTime := .Options.OutputResponseTime }}
{{ $OutputLinesCount := .Options.OutputLinesCount }}
{{ $OutputWordsCount := .Options.OutputWordsCount }}
{{ $OutputServerHeader := .Options.OutputServerHeader }}
{{ $TechDetect := .Options.TechDetect }}
{{range .Output}}
{{if ne .ScreenshotPath ""}}
<tr>
<td style="padding: 10px; border: 1px solid black">
<ul style="list-style-type: none; padding-left: 0">
<li>
<strong>Host:</strong>
<a style="text-decoration: none; color: blue">{{.URL}}</a>
</li>
{{if $ExtractTitle}}
<li>
<strong>Title:</strong>
<a style="text-decoration: none; color: blue">{{.Title}}</a>
</li>
{{end}}
{{if $OutputStatusCode}}
<li>
<strong>Status Code:</strong>
<a style="text-decoration: none; color: blue">{{.StatusCode}}</a>
</li>
{{end}}
{{if $OutputContentLength}}
<li>
<strong>Content-Length:</strong>
<a style="text-decoration: none; color: blue">{{.ContentLength}}</a>
</li>
{{end}}
{{if $Favicon}}
<li>
<strong>Favicon:</strong>
<a style="text-decoration: none; color: blue">{{.FavIconMMH3}}</a>
</li>
{{end}}
{{if $OutputResponseTime}}
<li>
<strong>Response Time:</strong>
<a style="text-decoration: none; color: blue">{{.ResponseTime}}</a>
</li>
{{end}}
{{if $OutputLinesCount}}
<li>
<strong>Total Lines:</strong>
<a style="text-decoration: none; color: blue">{{.Lines}}</a>
</li>
{{end}}
{{if $OutputWordsCount}}
<li>
<strong>Words Count:</strong>
<a style="text-decoration: none; color: blue">{{.Words}}</a>
</li>
{{end}}
{{if $OutputServerHeader}}
<li>
<strong>Webserver:</strong>
<a style="text-decoration: none; color: blue">{{.WebServer}}</a>
</li>
{{end}}
{{if $TechDetect}}
<li>
<strong>Technologies:</strong>
<a style="text-decoration: none; color: blue">{{.Technologies}}</a>
</li>
{{end}}
</ul>
</td>
<td style="padding: 10px; border: 1px solid black">
<a href="{{.ScreenshotPath | safeURL}}" target="_blank">
<img src="{{.ScreenshotPath | safeURL}}" alt="Screenshot" style="width: 400px; height: 300px" />
</a>
</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
</body>
</html>

8
static/static.go Normal file
View File

@ -0,0 +1,8 @@
package static
import (
_ "embed"
)
//go:embed html-summary.html
var HtmlTemplate string