Added matched-status flag + template-path and url to output (#1272)

* Added matched-status flag + template-path and url to output
dev
Ice3man 2021-11-22 17:53:25 +05:30 committed by GitHub
parent ca9676f52e
commit 1581c96e4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 205 additions and 66 deletions

View File

@ -85,6 +85,7 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "don't display match metadata"),
flagSet.BoolVarP(&options.NoTimestamp, "no-timestamp", "nts", false, "don't display timestamp metadata in CLI output"),
flagSet.StringVarP(&options.ReportingDB, "report-db", "rdb", "", "local nuclei reporting database (always use this to persist report data)"),
flagSet.BoolVarP(&options.MatcherStatus, "matcher-status", "ms", false, "show optional match failure status"),
flagSet.StringVarP(&options.MarkdownExportDirectory, "markdown-export", "me", "", "directory to export results in markdown format"),
flagSet.StringVarP(&options.SarifExport, "sarif-export", "se", "", "file to export results in SARIF format"),
)

View File

@ -117,7 +117,7 @@ func New(options *types.Options) (*Runner, error) {
runner.hmapInputProvider = hmapInput
// Create the output file if asked
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.NoTimestamp, options.JSON, options.JSONRequests, options.Output, options.TraceLogFile, options.ErrorLogFile)
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.NoTimestamp, options.JSON, options.JSONRequests, options.MatcherStatus, options.Output, options.TraceLogFile, options.ErrorLogFile)
if err != nil {
return nil, errors.Wrap(err, "could not create output file")
}

View File

@ -27,6 +27,15 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) []byte {
builder.WriteString(w.aurora.BrightGreen(output.ExtractorName).Bold().String())
}
if w.matcherStatus {
builder.WriteString("] [")
if !output.MatcherStatus {
builder.WriteString(w.aurora.Red("failed").String())
} else {
builder.WriteString(w.aurora.Green("matched").String())
}
}
builder.WriteString("] [")
builder.WriteString(w.aurora.BrightBlue(output.Type).String())
builder.WriteString("] ")
@ -35,7 +44,11 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) []byte {
builder.WriteString(w.severityColors(output.Info.SeverityHolder.Severity))
builder.WriteString("] ")
}
builder.WriteString(output.Matched)
if output.Matched != "" {
builder.WriteString(output.Matched)
} else {
builder.WriteString(output.Host)
}
// If any extractors, write the results
if len(output.ExtractedResults) > 0 {

View File

@ -16,6 +16,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/model"
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
)
@ -27,6 +28,8 @@ type Writer interface {
Colorizer() aurora.Aurora
// Write writes the event to file and/or screen.
Write(*ResultEvent) error
// WriteFailure writes the optional failure event for template to file and/or screen.
WriteFailure(event InternalEvent) error
// Request logs a request in the trace log
Request(templateID, url, requestType string, err error)
}
@ -37,6 +40,7 @@ type StandardWriter struct {
jsonReqResp bool
noTimestamp bool
noMetadata bool
matcherStatus bool
aurora aurora.Aurora
outputFile io.WriteCloser
traceFile io.WriteCloser
@ -54,10 +58,16 @@ type InternalWrappedEvent struct {
InternalEvent InternalEvent
Results []*ResultEvent
OperatorsResult *operators.Result
UsesInteractsh bool
}
// ResultEvent is a wrapped result event for a single nuclei output.
type ResultEvent struct {
// Template is the relative filename for the template
Template string `json:"template,omitempty"`
// TemplateURL is the URL of the template for the result inside the nuclei
// templates repository if it belongs to the repository.
TemplateURL string `json:"template-url,omitempty"`
// TemplateID is the ID of the template for the result.
TemplateID string `json:"template-id"`
// TemplatePath is the path of template
@ -92,12 +102,14 @@ type ResultEvent struct {
Interaction *server.Interaction `json:"interaction,omitempty"`
// CURLCommand is an optional curl command to reproduce the request
// Only applicable if the report is for HTTP.
CURLCommand string `json:"curl-command,omitempty"`
CURLCommand string `json:"curl-command,omitempty"`
// MatcherStatus is the status of the match
MatcherStatus bool `json:"matcher-status"`
FileToIndexPosition map[string]int `json:"-"`
}
// NewStandardWriter creates a new output writer based on user configurations
func NewStandardWriter(colors, noMetadata, noTimestamp, json, jsonReqResp bool, file, traceFile string, errorFile string) (*StandardWriter, error) {
func NewStandardWriter(colors, noMetadata, noTimestamp, json, jsonReqResp, MatcherStatus bool, file, traceFile string, errorFile string) (*StandardWriter, error) {
auroraColorizer := aurora.NewAurora(colors)
var outputFile io.WriteCloser
@ -128,6 +140,7 @@ func NewStandardWriter(colors, noMetadata, noTimestamp, json, jsonReqResp bool,
json: json,
jsonReqResp: jsonReqResp,
noMetadata: noMetadata,
matcherStatus: MatcherStatus,
noTimestamp: noTimestamp,
aurora: auroraColorizer,
outputFile: outputFile,
@ -140,6 +153,10 @@ func NewStandardWriter(colors, noMetadata, noTimestamp, json, jsonReqResp bool,
// Write writes the event to file and/or screen.
func (w *StandardWriter) Write(event *ResultEvent) error {
// Enrich the result event with extra metadata on the template-path and url.
if event.TemplatePath != "" {
event.Template, event.TemplateURL = utils.TemplatePathURL(types.ToString(event.TemplatePath))
}
event.Timestamp = time.Now()
var data []byte
@ -224,3 +241,23 @@ func (w *StandardWriter) Close() {
w.errorFile.Close()
}
}
// WriteFailure writes the failure event for template to file and/or screen.
func (w *StandardWriter) WriteFailure(event InternalEvent) error {
if !w.matcherStatus {
return nil
}
templatePath, templateURL := utils.TemplatePathURL(types.ToString(event["template-path"]))
data := &ResultEvent{
Template: templatePath,
TemplateURL: templateURL,
TemplateID: types.ToString(event["template-id"]),
TemplatePath: types.ToString(event["template-path"]),
Info: event["template-info"].(model.Info),
Type: types.ToString(event["type"]),
Host: types.ToString(event["host"]),
MatcherStatus: false,
Timestamp: time.Now(),
}
return w.Write(data)
}

View File

@ -11,7 +11,7 @@ import (
func TestStandardWriterRequest(t *testing.T) {
t.Run("WithoutTraceAndError", func(t *testing.T) {
w, err := NewStandardWriter(false, false, false, false, false, "", "", "")
w, err := NewStandardWriter(false, false, false, false, false, false, "", "", "")
require.NoError(t, err)
require.NotPanics(t, func() {
w.Request("path", "input", "http", nil)
@ -23,7 +23,7 @@ func TestStandardWriterRequest(t *testing.T) {
traceWriter := &testWriteCloser{}
errorWriter := &testWriteCloser{}
w, err := NewStandardWriter(false, false, false, false, false, "", "", "")
w, err := NewStandardWriter(false, false, false, false, false, false, "", "", "")
w.traceFile = traceWriter
w.errorFile = errorWriter
require.NoError(t, err)
@ -36,7 +36,7 @@ func TestStandardWriterRequest(t *testing.T) {
t.Run("ErrorWithWrappedError", func(t *testing.T) {
errorWriter := &testWriteCloser{}
w, err := NewStandardWriter(false, false, false, false, false, "", "", "")
w, err := NewStandardWriter(false, false, false, false, false, false, "", "", "")
w.errorFile = errorWriter
require.NoError(t, err)
w.Request(

View File

@ -6,6 +6,7 @@ import (
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/writer"
)
// Executer executes a group of requests for a protocol
@ -59,18 +60,17 @@ func (e *Executer) Execute(input string) (bool, error) {
builder.Reset()
}
}
if event.OperatorsResult == nil {
return
}
for _, result := range event.Results {
if e.options.IssuesClient != nil {
if err := e.options.IssuesClient.CreateIssue(result); err != nil {
gologger.Warning().Msgf("Could not create issue on tracker: %s", err)
}
// If no results were found, and also interactsh is not being used
// in that case we can skip it, otherwise we've to show failure in
// case of matcher-status flag.
if event.OperatorsResult == nil && !event.UsesInteractsh {
if err := e.options.Output.WriteFailure(event.InternalEvent); err != nil {
gologger.Warning().Msgf("Could not write failure event to output: %s\n", err)
}
} else {
if writer.WriteResult(event, e.options.Output, e.options.Progress, e.options.IssuesClient) {
results = true
}
results = true
_ = e.options.Output.Write(result)
e.options.Progress.IncrementMatched()
}
})
if err != nil {

View File

@ -0,0 +1,35 @@
package writer
import (
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/progress"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting"
)
// WriteResult is a helper for writing results to the output
func WriteResult(data *output.InternalWrappedEvent, output output.Writer, progress progress.Progress, issuesClient *reporting.Client) bool {
// Handle the case where no result found for the template.
// In this case, we just show misc information about the failed
// match for the template.
if data.OperatorsResult == nil {
return false
}
var matched bool
for _, result := range data.Results {
if err := output.Write(result); err != nil {
gologger.Warning().Msgf("Could not write output event: %s\n", err)
}
if !matched {
matched = true
}
progress.IncrementMatched()
if issuesClient != nil {
if err := issuesClient.CreateIssue(result); err != nil {
gologger.Warning().Msgf("Could not create issue on tracker: %s", err)
}
}
}
return matched
}

View File

@ -19,6 +19,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/progress"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/writer"
"github.com/projectdiscovery/nuclei/v2/pkg/reporting"
)
@ -177,19 +178,8 @@ func (c *Client) processInteractionForRequest(interaction *server.Interaction, d
}
data.Event.Results = data.MakeResultFunc(data.Event)
for _, result := range data.Event.Results {
result.Interaction = interaction
_ = c.options.Output.Write(result)
if !c.matched {
c.matched = true
}
c.options.Progress.IncrementMatched()
if c.options.IssuesClient != nil {
if err := c.options.IssuesClient.CreateIssue(result); err != nil {
gologger.Warning().Msgf("Could not create issue on tracker: %s", err)
}
}
if writer.WriteResult(data.Event, c.options.Output, c.options.Progress, c.options.IssuesClient) {
c.matched = true
}
return true
}

View File

@ -90,6 +90,7 @@ func (request *Request) responseToDSLMap(req, resp *dns.Msg, host, matched strin
"template-id": request.options.TemplateID,
"template-info": request.options.TemplateInfo,
"template-path": request.options.TemplatePath,
"type": request.Type().String(),
"trace": traceToString(tracedata, false),
}
}
@ -104,10 +105,11 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
Info: wrapped.InternalEvent["template-info"].(model.Info),
Type: "dns",
Type: types.ToString(wrapped.InternalEvent["type"]),
Host: types.ToString(wrapped.InternalEvent["host"]),
Matched: types.ToString(wrapped.InternalEvent["matched"]),
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
MatcherStatus: true,
Timestamp: time.Now(),
Request: types.ToString(wrapped.InternalEvent["request"]),
Response: types.ToString(wrapped.InternalEvent["raw"]),

View File

@ -45,7 +45,7 @@ func TestResponseToDSLMap(t *testing.T) {
resp.Answer = append(resp.Answer, &dns.A{A: net.ParseIP("1.1.1.1"), Hdr: dns.RR_Header{Name: "one.one.one.one."}})
event := request.responseToDSLMap(req, resp, "one.one.one.one", "one.one.one.one", nil)
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, dns.RcodeSuccess, event["rcode"], "could not get correct rcode")
}

View File

@ -73,6 +73,7 @@ func (request *Request) responseToDSLMap(raw, inputFilePath, matchedFileName str
"path": inputFilePath,
"matched": matchedFileName,
"raw": raw,
"type": request.Type().String(),
"template-id": request.options.TemplateID,
"template-info": request.options.TemplateInfo,
"template-path": request.options.TemplatePath,
@ -120,10 +121,11 @@ func (request *Request) GetCompiledOperators() []*operators.Operators {
func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
data := &output.ResultEvent{
MatcherStatus: true,
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
Info: wrapped.InternalEvent["template-info"].(model.Info),
Type: "file",
Type: types.ToString(wrapped.InternalEvent["type"]),
Path: types.ToString(wrapped.InternalEvent["path"]),
Matched: types.ToString(wrapped.InternalEvent["matched"]),
Host: types.ToString(wrapped.InternalEvent["host"]),

View File

@ -35,7 +35,7 @@ func TestResponseToDSLMap(t *testing.T) {
resp := "test-data\r\n"
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
require.Len(t, event, 6, "could not get correct number of items in dsl map")
require.Len(t, event, 7, "could not get correct number of items in dsl map")
require.Equal(t, resp, event["raw"], "could not get correct resp")
}
@ -60,7 +60,7 @@ func TestFileOperatorMatch(t *testing.T) {
resp := "test-data\r\n1.1.1.1\r\n"
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
require.Len(t, event, 6, "could not get correct number of items in dsl map")
require.Len(t, event, 7, "could not get correct number of items in dsl map")
require.Equal(t, resp, event["raw"], "could not get correct resp")
t.Run("valid", func(t *testing.T) {
@ -109,7 +109,7 @@ func TestFileOperatorMatch(t *testing.T) {
t.Run("caseInsensitive", func(t *testing.T) {
resp := "TEST-DATA\r\n1.1.1.1\r\n"
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
require.Len(t, event, 6, "could not get correct number of items in dsl map")
require.Len(t, event, 7, "could not get correct number of items in dsl map")
require.Equal(t, resp, event["raw"], "could not get correct resp")
matcher := &matchers.Matcher{
@ -148,7 +148,7 @@ func TestFileOperatorExtract(t *testing.T) {
resp := "test-data\r\n1.1.1.1\r\n"
event := request.responseToDSLMap(resp, "one.one.one.one", "one.one.one.one")
require.Len(t, event, 6, "could not get correct number of items in dsl map")
require.Len(t, event, 7, "could not get correct number of items in dsl map")
require.Equal(t, resp, event["raw"], "could not get correct resp")
t.Run("extract", func(t *testing.T) {
@ -266,7 +266,7 @@ func testFileMakeResult(t *testing.T, matchers []*matchers.Matcher, matcherCondi
fileContent := "test-data\r\n1.1.1.1\r\n"
event := request.responseToDSLMap(fileContent, "/tmp", matchedFileName)
require.Len(t, event, 6, "could not get correct number of items in dsl map")
require.Len(t, event, 7, "could not get correct number of items in dsl map")
require.Equal(t, fileContent, event["raw"], "could not get correct resp")
finalEvent := &output.InternalWrappedEvent{InternalEvent: event}

View File

@ -72,6 +72,7 @@ func (request *Request) responseToDSLMap(resp, req, host, matched string) output
"matched": matched,
"req": req,
"data": resp,
"type": request.Type().String(),
"template-id": request.options.TemplateID,
"template-info": request.options.TemplateInfo,
"template-path": request.options.TemplatePath,
@ -92,11 +93,12 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
Info: wrapped.InternalEvent["template-info"].(model.Info),
Type: "headless",
Type: types.ToString(wrapped.InternalEvent["type"]),
Host: types.ToString(wrapped.InternalEvent["host"]),
Matched: types.ToString(wrapped.InternalEvent["matched"]),
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
Timestamp: time.Now(),
MatcherStatus: true,
IP: types.ToString(wrapped.InternalEvent["ip"]),
Request: types.ToString(wrapped.InternalEvent["request"]),
Response: types.ToString(wrapped.InternalEvent["data"]),

View File

@ -113,6 +113,7 @@ func (request *Request) responseToDSLMap(resp *http.Response, host, matched, raw
data[k] = strings.Join(v, " ")
}
data["host"] = host
data["type"] = request.Type().String()
data["matched"] = matched
data["request"] = rawReq
data["response"] = rawResp
@ -141,12 +142,13 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
Info: wrapped.InternalEvent["template-info"].(model.Info),
Type: "http",
Type: types.ToString(wrapped.InternalEvent["type"]),
Host: types.ToString(wrapped.InternalEvent["host"]),
Matched: types.ToString(wrapped.InternalEvent["matched"]),
Metadata: wrapped.OperatorsResult.PayloadValues,
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
Timestamp: time.Now(),
MatcherStatus: true,
IP: types.ToString(wrapped.InternalEvent["ip"]),
Request: types.ToString(wrapped.InternalEvent["request"]),
Response: types.ToString(wrapped.InternalEvent["response"]),

View File

@ -41,7 +41,7 @@ func TestResponseToDSLMap(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
}
@ -71,7 +71,7 @@ func TestHTTPOperatorMatch(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
@ -159,7 +159,7 @@ func TestHTTPOperatorExtract(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test_header"], "could not get correct resp for header")
@ -286,7 +286,7 @@ func TestHTTPMakeResult(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")

View File

@ -473,6 +473,9 @@ func (request *Request) executeRequest(reqURL string, generatedRequest *generate
event := eventcreator.CreateEventWithAdditionalOptions(request, finalEvent, request.options.Options.Debug || request.options.Options.DebugResponse, func(internalWrappedEvent *output.InternalWrappedEvent) {
internalWrappedEvent.OperatorsResult.PayloadValues = generatedRequest.meta
})
if hasInteractMarkers {
event.UsesInteractsh = true
}
responseContentType := resp.Header.Get("Content-Type")
dumpResponse(event, request.options, response.fullResponse, formedURL, responseContentType)

View File

@ -73,6 +73,7 @@ func (request *Request) responseToDSLMap(req, resp, raw, host, matched string) o
"request": req,
"data": resp, // Data is the last bytes read
"raw": raw, // Raw is the full transaction data for network
"type": request.Type().String(),
"template-id": request.options.TemplateID,
"template-info": request.options.TemplateInfo,
"template-path": request.options.TemplatePath,
@ -93,12 +94,13 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
Info: wrapped.InternalEvent["template-info"].(model.Info),
Type: "network",
Type: types.ToString(wrapped.InternalEvent["type"]),
Host: types.ToString(wrapped.InternalEvent["host"]),
Matched: types.ToString(wrapped.InternalEvent["matched"]),
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
Metadata: wrapped.OperatorsResult.PayloadValues,
Timestamp: time.Now(),
MatcherStatus: true,
IP: types.ToString(wrapped.InternalEvent["ip"]),
Request: types.ToString(wrapped.InternalEvent["request"]),
Response: types.ToString(wrapped.InternalEvent["data"]),

View File

@ -35,7 +35,7 @@ func TestResponseToDSLMap(t *testing.T) {
req := "test-data\r\n"
resp := "resp-data\r\n"
event := request.responseToDSLMap(req, resp, "test", "one.one.one.one", "one.one.one.one")
require.Len(t, event, 8, "could not get correct number of items in dsl map")
require.Len(t, event, 9, "could not get correct number of items in dsl map")
require.Equal(t, resp, event["data"], "could not get correct resp")
}

View File

@ -281,6 +281,9 @@ func (request *Request) executeRequestWithPayloads(actualAddress, address, input
ExtractFunc: request.Extract,
})
}
if len(interactshURLs) > 0 {
event.UsesInteractsh = true
}
dumpResponse(event, request.options, response, actualAddress)

View File

@ -113,6 +113,7 @@ func (request *Request) responseToDSLMap(resp *http.Response, host, matched, raw
data["content_length"] = resp.ContentLength
data["status_code"] = resp.StatusCode
data["body"] = body
data["type"] = request.Type().String()
data["all_headers"] = headers
data["duration"] = duration.Seconds()
data["template-id"] = request.options.TemplateID
@ -135,11 +136,12 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
Info: wrapped.InternalEvent["template-info"].(model.Info),
Type: "http",
Type: types.ToString(wrapped.InternalEvent["type"]),
Path: types.ToString(wrapped.InternalEvent["path"]),
Matched: types.ToString(wrapped.InternalEvent["matched"]),
Metadata: wrapped.OperatorsResult.PayloadValues,
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
MatcherStatus: true,
IP: types.ToString(wrapped.InternalEvent["ip"]),
Request: types.ToString(wrapped.InternalEvent["request"]),
Response: types.ToString(wrapped.InternalEvent["raw"]),

View File

@ -37,7 +37,7 @@ func TestResponseToDSLMap(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
}
@ -63,7 +63,7 @@ func TestHTTPOperatorMatch(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")
@ -132,7 +132,7 @@ func TestHTTPOperatorExtract(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test-header"], "could not get correct resp for header")
@ -198,7 +198,7 @@ func TestHTTPMakeResult(t *testing.T) {
matched := "http://example.com/test/?test=1"
event := request.responseToDSLMap(resp, host, matched, exampleRawRequest, exampleRawResponse, exampleResponseBody, exampleResponseHeader, 1*time.Second, map[string]interface{}{})
require.Len(t, event, 13, "could not get correct number of items in dsl map")
require.Len(t, event, 14, "could not get correct number of items in dsl map")
require.Equal(t, exampleRawResponse, event["response"], "could not get correct resp")
require.Equal(t, "Test-Response", event["test"], "could not get correct resp for header")

View File

@ -129,6 +129,7 @@ func (request *Request) ExecuteWithResults(input string, dynamicValues, previous
data := make(map[string]interface{})
cert := connTLS.ConnectionState().PeerCertificates[0]
data["type"] = request.Type().String()
data["response"] = jsonDataString
data["host"] = input
data["matched"] = addressToDial
@ -195,12 +196,13 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
TemplateID: types.ToString(request.options.TemplateID),
TemplatePath: types.ToString(request.options.TemplatePath),
Info: request.options.TemplateInfo,
Type: request.Type().String(),
Type: types.ToString(wrapped.InternalEvent["type"]),
Host: types.ToString(wrapped.InternalEvent["host"]),
Matched: types.ToString(wrapped.InternalEvent["host"]),
Metadata: wrapped.OperatorsResult.PayloadValues,
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
Timestamp: time.Now(),
MatcherStatus: true,
IP: types.ToString(wrapped.InternalEvent["ip"]),
}
return data

View File

@ -248,6 +248,8 @@ func (request *Request) executeRequestWithPayloads(input, hostname string, dynam
for k, v := range events {
data[k] = v
}
data["type"] = request.Type().String()
data["success"] = "true"
data["request"] = requestOutput
data["response"] = responseBuilder.String()
@ -364,12 +366,13 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
TemplateID: types.ToString(request.options.TemplateID),
TemplatePath: types.ToString(request.options.TemplatePath),
Info: request.options.TemplateInfo,
Type: request.Type().String(),
Type: types.ToString(wrapped.InternalEvent["type"]),
Host: types.ToString(wrapped.InternalEvent["host"]),
Matched: types.ToString(wrapped.InternalEvent["matched"]),
Metadata: wrapped.OperatorsResult.PayloadValues,
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
Timestamp: time.Now(),
MatcherStatus: true,
IP: types.ToString(wrapped.InternalEvent["ip"]),
Request: types.ToString(wrapped.InternalEvent["request"]),
Response: types.ToString(wrapped.InternalEvent["response"]),

View File

@ -8,6 +8,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/writer"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http"
"github.com/rs/xid"
)
@ -147,22 +148,22 @@ func (e *Executer) Execute(input string) (bool, error) {
err := e.requests.ExecuteWithResults(input, dynamicValues, previous, func(event *output.InternalWrappedEvent) {
for _, operator := range e.operators {
result, matched := operator.operator.Execute(event.InternalEvent, e.requests.Match, e.requests.Extract, e.options.Options.Debug || e.options.Options.DebugResponse)
event.InternalEvent["template-id"] = operator.templateID
event.InternalEvent["template-path"] = operator.templatePath
event.InternalEvent["template-info"] = operator.templateInfo
if result == nil && !matched {
if err := e.options.Output.WriteFailure(event.InternalEvent); err != nil {
gologger.Warning().Msgf("Could not write failure event to output: %s\n", err)
}
continue
}
if matched && result != nil {
event.OperatorsResult = result
event.InternalEvent["template-id"] = operator.templateID
event.InternalEvent["template-path"] = operator.templatePath
event.InternalEvent["template-info"] = operator.templateInfo
event.Results = e.requests.MakeResultEvent(event)
results = true
for _, r := range event.Results {
if e.options.IssuesClient != nil {
if err := e.options.IssuesClient.CreateIssue(r); err != nil {
gologger.Warning().Msgf("Could not create issue on tracker: %s", err)
}
}
_ = e.options.Output.Write(r)
e.options.Progress.IncrementMatched()
}
_ = writer.WriteResult(event, e.options.Output, e.options.Progress, e.options.IssuesClient)
}
}
})

View File

@ -131,6 +131,11 @@ func (m *MockOutputWriter) Request(templateID, url, requestType string, err erro
}
}
// Write writes the event to file and/or screen.
func (m *MockOutputWriter) WriteFailure(result output.InternalEvent) error {
return nil
}
type MockProgressClient struct{}
// Stop stops the progress recorder.

View File

@ -177,6 +177,8 @@ type Options struct {
NoUpdateTemplates bool
// EnvironmentVariables enables support for environment variables
EnvironmentVariables bool
// MatcherStatus displays optional status for the failed matches as well
MatcherStatus bool
// ClientCertFile client certificate file (PEM-encoded) used for authenticating against scanned hosts
ClientCertFile string
// ClientKeyFile client key file (PEM-encoded) used for authenticating against scanned hosts

View File

@ -0,0 +1,32 @@
package utils
import (
"strings"
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
)
const (
// TemplatesRepoURL is the URL for files in nuclei-templates repository
TemplatesRepoURL = "https://github.com/projectdiscovery/nuclei-templates/blob/master/"
)
var configData *config.Config
func init() {
configData, _ = config.ReadConfiguration()
}
// TemplatePathURL returns the Path and URL for the provided template
func TemplatePathURL(fullPath string) (string, string) {
var templateDirectory string
if configData != nil && configData.TemplatesDirectory != "" && strings.HasPrefix(fullPath, configData.TemplatesDirectory) {
templateDirectory = configData.TemplatesDirectory
} else {
return "", ""
}
finalPath := strings.TrimPrefix(strings.TrimPrefix(fullPath, templateDirectory), "/")
templateURL := TemplatesRepoURL + finalPath
return finalPath, templateURL
}