mirror of
https://github.com/projectdiscovery/httpx.git
synced 2024-09-11 09:35:28 +03:00
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:
parent
f5c5fc63e1
commit
12db59a1d0
@ -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
133
static/html-summary.html
Normal 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
8
static/static.go
Normal file
@ -0,0 +1,8 @@
|
||||
package static
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed html-summary.html
|
||||
var HtmlTemplate string
|
Loading…
Reference in New Issue
Block a user