More tests + tag based execution + misc

This commit is contained in:
Ice3man543 2021-02-04 01:09:29 +05:30
parent a23ca6ee54
commit f2c20dda12
30 changed files with 257 additions and 172 deletions

View File

@ -74,6 +74,7 @@ based on templates offering massive extensibility and ease of use.`)
set.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "Don't display metadata for the matches") set.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "Don't display metadata for the matches")
set.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "Shows the installed nuclei-templates version") set.BoolVarP(&options.TemplatesVersion, "templates-version", "tv", false, "Shows the installed nuclei-templates version")
set.StringVarP(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "biid", "", "Burp Collaborator BIID") set.StringVarP(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "biid", "", "Burp Collaborator BIID")
set.StringSliceVar(&options.Tags, "tags", []string{}, "Tags to execute templates for")
_ = set.Parse() _ = set.Parse()
if cfgFile != "" { if cfgFile != "" {

View File

@ -29,10 +29,10 @@ func (r *Runner) getParsedTemplatesFor(templatePaths []string, severities []stri
if len(t.Workflows) > 0 { if len(t.Workflows) > 0 {
workflowCount++ workflowCount++
} }
sev := strings.ToLower(t.Info["severity"]) sev := strings.ToLower(t.Info["severity"].(string))
if !filterBySeverity || hasMatchingSeverity(sev, severities) { if !filterBySeverity || hasMatchingSeverity(sev, severities) {
parsedTemplates[t.ID] = t parsedTemplates[t.ID] = t
gologger.Info().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"], t.Info["author"], t.Info["severity"])) gologger.Info().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"].(string), t.Info["author"].(string), t.Info["severity"].(string)))
} else { } else {
gologger.Error().Msgf("Excluding template %s due to severity filter (%s not in [%s])", t.ID, sev, severities) gologger.Error().Msgf("Excluding template %s due to severity filter (%s not in [%s])", t.ID, sev, severities)
} }
@ -74,7 +74,7 @@ func (r *Runner) logAvailableTemplate(tplPath string) {
if err != nil { if err != nil {
gologger.Error().Msgf("Could not parse file '%s': %s\n", tplPath, err) gologger.Error().Msgf("Could not parse file '%s': %s\n", tplPath, err)
} else { } else {
gologger.Print().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"], t.Info["author"], t.Info["severity"])) gologger.Print().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"].(string), t.Info["author"].(string), t.Info["severity"].(string)))
} }
} }

View File

@ -96,7 +96,7 @@ func (m *MockOutputWriter) Request(templateID, url, requestType string, err erro
// TemplateInfo contains info for a mock executed template. // TemplateInfo contains info for a mock executed template.
type TemplateInfo struct { type TemplateInfo struct {
ID string ID string
Info map[string]string Info map[string]interface{}
Path string Path string
} }

View File

@ -27,7 +27,7 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) ([]byte, error) {
builder.WriteString("] ") builder.WriteString("] ")
builder.WriteString("[") builder.WriteString("[")
builder.WriteString(w.severityColors.Data[output.Info["severity"]]) builder.WriteString(w.severityColors.Data[output.Info["severity"].(string)])
builder.WriteString("] ") builder.WriteString("] ")
} }
builder.WriteString(output.Matched) builder.WriteString(output.Matched)

View File

@ -54,7 +54,7 @@ type ResultEvent struct {
// TemplateID is the ID of the template for the result. // TemplateID is the ID of the template for the result.
TemplateID string `json:"templateID"` TemplateID string `json:"templateID"`
// Info contains information block of the template for the result. // Info contains information block of the template for the result.
Info map[string]string `json:"info,inline"` Info map[string]interface{} `json:"info,inline"`
// MatcherName is the name of the matcher matched if any. // MatcherName is the name of the matcher matched if any.
MatcherName string `json:"matcher_name,omitempty"` MatcherName string `json:"matcher_name,omitempty"`
// ExtractorName is the name of the extractor matched if any. // ExtractorName is the name of the extractor matched if any.

View File

@ -21,7 +21,7 @@ type Executer struct {
type clusteredOperator struct { type clusteredOperator struct {
templateID string templateID string
templateInfo map[string]string templateInfo map[string]interface{}
operator *operators.Operators operator *operators.Operators
} }

View File

@ -22,7 +22,7 @@ func TestDNSCompileMake(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile dns request") require.Nil(t, err, "could not compile dns request")

View File

@ -136,7 +136,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent { func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
data := &output.ResultEvent{ data := &output.ResultEvent{
TemplateID: wrapped.InternalEvent["template-id"].(string), TemplateID: wrapped.InternalEvent["template-id"].(string),
Info: wrapped.InternalEvent["template-info"].(map[string]string), Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
Type: "dns", Type: "dns",
Host: wrapped.InternalEvent["host"].(string), Host: wrapped.InternalEvent["host"].(string),
Matched: wrapped.InternalEvent["matched"].(string), Matched: wrapped.InternalEvent["matched"].(string),

View File

@ -29,7 +29,7 @@ func TestResponseToDSLMap(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile dns request") require.Nil(t, err, "could not compile dns request")
@ -61,7 +61,7 @@ func TestDNSOperatorMatch(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile dns request") require.Nil(t, err, "could not compile dns request")
@ -144,7 +144,7 @@ func TestDNSOperatorExtract(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile dns request") require.Nil(t, err, "could not compile dns request")
@ -214,7 +214,7 @@ func TestDNSMakeResult(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile dns request") require.Nil(t, err, "could not compile dns request")

View File

@ -39,7 +39,7 @@ func TestDNSExecuteWithResults(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile dns request") require.Nil(t, err, "could not compile dns request")

View File

@ -22,7 +22,7 @@ func TestFileCompile(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile file request") require.Nil(t, err, "could not compile file request")

View File

@ -25,7 +25,7 @@ func TestFindInputPaths(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile file request") require.Nil(t, err, "could not compile file request")

View File

@ -103,7 +103,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent { func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
data := &output.ResultEvent{ data := &output.ResultEvent{
TemplateID: wrapped.InternalEvent["template-id"].(string), TemplateID: wrapped.InternalEvent["template-id"].(string),
Info: wrapped.InternalEvent["template-info"].(map[string]string), Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
Type: "file", Type: "file",
Host: wrapped.InternalEvent["host"].(string), Host: wrapped.InternalEvent["host"].(string),
Matched: wrapped.InternalEvent["matched"].(string), Matched: wrapped.InternalEvent["matched"].(string),

View File

@ -26,7 +26,7 @@ func TestResponseToDSLMap(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile file request") require.Nil(t, err, "could not compile file request")
@ -52,7 +52,7 @@ func TestFileOperatorMatch(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile file request") require.Nil(t, err, "could not compile file request")
@ -118,7 +118,7 @@ func TestFileOperatorExtract(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile file request") require.Nil(t, err, "could not compile file request")
@ -184,7 +184,7 @@ func TestFileMakeResult(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile file request") require.Nil(t, err, "could not compile file request")

View File

@ -42,7 +42,7 @@ func TestFileExecuteWithResults(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile file request") require.Nil(t, err, "could not compile file request")

View File

@ -27,72 +27,6 @@ var (
templateExpressionRegex = regexp.MustCompile(`(?m)\{\{[^}]+\}\}`) templateExpressionRegex = regexp.MustCompile(`(?m)\{\{[^}]+\}\}`)
) )
// requestGenerator generates requests sequentially based on various
// configurations for a http request template.
//
// If payload values are present, an iterator is created for the payload
// values. Paths and Raw requests are supported as base input, so
// it will automatically select between them based on the template.
type requestGenerator struct {
currentIndex int
request *Request
payloadIterator *generators.Iterator
}
// newGenerator creates a new request generator instance
func (r *Request) newGenerator() *requestGenerator {
generator := &requestGenerator{request: r}
if len(r.Payloads) > 0 {
generator.payloadIterator = r.generator.NewIterator()
}
return generator
}
// nextValue returns the next path or the next raw request depending on user input
// It returns false if all the inputs have been exhausted by the generator instance.
func (r *requestGenerator) nextValue() (string, map[string]interface{}, bool) {
// If we have paths, return the next path.
if len(r.request.Path) > 0 && r.currentIndex < len(r.request.Path) {
if item := r.request.Path[r.currentIndex]; item != "" {
r.currentIndex++
return item, nil, true
}
}
// If we have raw requests, start with the request at current index.
// If we are not at the start, then check if the iterator for payloads
// has finished if there are any.
//
// If the iterator has finished for the current raw request
// then reset it and move on to the next value, otherwise use the last request.
if len(r.request.Raw) > 0 && r.currentIndex < len(r.request.Raw) {
if r.payloadIterator != nil {
payload, ok := r.payloadIterator.Value()
if !ok {
r.currentIndex++
r.payloadIterator.Reset()
// No more payloads request for us now.
if len(r.request.Raw) == r.currentIndex {
return "", nil, false
}
if item := r.request.Raw[r.currentIndex]; item != "" {
newPayload, ok := r.payloadIterator.Value()
return item, newPayload, ok
}
return "", nil, false
}
return r.request.Raw[r.currentIndex], payload, true
}
if item := r.request.Raw[r.currentIndex]; item != "" {
r.currentIndex++
return item, nil, true
}
}
return "", nil, false
}
// generatedRequest is a single wrapped generated request for a template request // generatedRequest is a single wrapped generated request for a template request
type generatedRequest struct { type generatedRequest struct {
original *Request original *Request

View File

@ -1,70 +1 @@
package http package http
import (
"testing"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
"github.com/stretchr/testify/require"
)
func TestRequestGeneratorPaths(t *testing.T) {
req := &Request{
Path: []string{"{{BaseURL}}/test", "{{BaseURL}}/test.php"},
}
generator := req.newGenerator()
var payloads []string
for {
raw, _, ok := generator.nextValue()
if !ok {
break
}
payloads = append(payloads, raw)
}
require.Equal(t, req.Path, payloads, "Could not get correct paths")
}
func TestRequestGeneratorClusterSingle(t *testing.T) {
var err error
req := &Request{
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
attackType: generators.ClusterBomb,
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`},
}
req.generator, err = generators.New(req.Payloads, req.attackType, "")
require.Nil(t, err, "could not create generator")
generator := req.newGenerator()
var payloads []map[string]interface{}
for {
_, data, ok := generator.nextValue()
if !ok {
break
}
payloads = append(payloads, data)
}
require.Equal(t, 9, len(payloads), "Could not get correct number of payloads")
}
func TestRequestGeneratorClusterMultipleRaw(t *testing.T) {
var err error
req := &Request{
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
attackType: generators.ClusterBomb,
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`, `GET /{{username}}@{{password}} HTTP/1.1`},
}
req.generator, err = generators.New(req.Payloads, req.attackType, "")
require.Nil(t, err, "could not create generator")
generator := req.newGenerator()
var payloads []map[string]interface{}
for {
_, data, ok := generator.nextValue()
if !ok {
break
}
payloads = append(payloads, data)
}
require.Equal(t, 18, len(payloads), "Could not get correct number of payloads")
}

View File

@ -0,0 +1 @@
package http

View File

@ -136,7 +136,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent { func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
data := &output.ResultEvent{ data := &output.ResultEvent{
TemplateID: wrapped.InternalEvent["template-id"].(string), TemplateID: wrapped.InternalEvent["template-id"].(string),
Info: wrapped.InternalEvent["template-info"].(map[string]string), Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
Type: "http", Type: "http",
Host: wrapped.InternalEvent["host"].(string), Host: wrapped.InternalEvent["host"].(string),
Matched: wrapped.InternalEvent["matched"].(string), Matched: wrapped.InternalEvent["matched"].(string),

View File

@ -0,0 +1,69 @@
package http
import "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
// requestGenerator generates requests sequentially based on various
// configurations for a http request template.
//
// If payload values are present, an iterator is created for the payload
// values. Paths and Raw requests are supported as base input, so
// it will automatically select between them based on the template.
type requestGenerator struct {
currentIndex int
request *Request
payloadIterator *generators.Iterator
}
// newGenerator creates a new request generator instance
func (r *Request) newGenerator() *requestGenerator {
generator := &requestGenerator{request: r}
if len(r.Payloads) > 0 {
generator.payloadIterator = r.generator.NewIterator()
}
return generator
}
// nextValue returns the next path or the next raw request depending on user input
// It returns false if all the inputs have been exhausted by the generator instance.
func (r *requestGenerator) nextValue() (string, map[string]interface{}, bool) {
// If we have paths, return the next path.
if len(r.request.Path) > 0 && r.currentIndex < len(r.request.Path) {
if item := r.request.Path[r.currentIndex]; item != "" {
r.currentIndex++
return item, nil, true
}
}
// If we have raw requests, start with the request at current index.
// If we are not at the start, then check if the iterator for payloads
// has finished if there are any.
//
// If the iterator has finished for the current raw request
// then reset it and move on to the next value, otherwise use the last request.
if len(r.request.Raw) > 0 && r.currentIndex < len(r.request.Raw) {
if r.payloadIterator != nil {
payload, ok := r.payloadIterator.Value()
if !ok {
r.currentIndex++
r.payloadIterator.Reset()
// No more payloads request for us now.
if len(r.request.Raw) == r.currentIndex {
return "", nil, false
}
if item := r.request.Raw[r.currentIndex]; item != "" {
newPayload, ok := r.payloadIterator.Value()
return item, newPayload, ok
}
return "", nil, false
}
return r.request.Raw[r.currentIndex], payload, true
}
if item := r.request.Raw[r.currentIndex]; item != "" {
r.currentIndex++
return item, nil, true
}
}
return "", nil, false
}

View File

@ -0,0 +1,70 @@
package http
import (
"testing"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
"github.com/stretchr/testify/require"
)
func TestRequestGeneratorPaths(t *testing.T) {
req := &Request{
Path: []string{"{{BaseURL}}/test", "{{BaseURL}}/test.php"},
}
generator := req.newGenerator()
var payloads []string
for {
raw, _, ok := generator.nextValue()
if !ok {
break
}
payloads = append(payloads, raw)
}
require.Equal(t, req.Path, payloads, "Could not get correct paths")
}
func TestRequestGeneratorClusterBombSingle(t *testing.T) {
var err error
req := &Request{
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
attackType: generators.ClusterBomb,
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`},
}
req.generator, err = generators.New(req.Payloads, req.attackType, "")
require.Nil(t, err, "could not create generator")
generator := req.newGenerator()
var payloads []map[string]interface{}
for {
_, data, ok := generator.nextValue()
if !ok {
break
}
payloads = append(payloads, data)
}
require.Equal(t, 9, len(payloads), "Could not get correct number of payloads")
}
func TestRequestGeneratorClusterBombMultipleRaw(t *testing.T) {
var err error
req := &Request{
Payloads: map[string]interface{}{"username": []string{"admin", "tomcat", "manager"}, "password": []string{"password", "test", "secret"}},
attackType: generators.ClusterBomb,
Raw: []string{`GET /{{username}}:{{password}} HTTP/1.1`, `GET /{{username}}@{{password}} HTTP/1.1`},
}
req.generator, err = generators.New(req.Payloads, req.attackType, "")
require.Nil(t, err, "could not create generator")
generator := req.newGenerator()
var payloads []map[string]interface{}
for {
_, data, ok := generator.nextValue()
if !ok {
break
}
payloads = append(payloads, data)
}
require.Equal(t, 18, len(payloads), "Could not get correct number of payloads")
}

View File

@ -20,7 +20,7 @@ func TestNetworkCompileMake(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile network request") require.Nil(t, err, "could not compile network request")

View File

@ -104,7 +104,7 @@ func (r *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*outpu
func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent { func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
data := &output.ResultEvent{ data := &output.ResultEvent{
TemplateID: wrapped.InternalEvent["template-id"].(string), TemplateID: wrapped.InternalEvent["template-id"].(string),
Info: wrapped.InternalEvent["template-info"].(map[string]string), Info: wrapped.InternalEvent["template-info"].(map[string]interface{}),
Type: "network", Type: "network",
Host: wrapped.InternalEvent["host"].(string), Host: wrapped.InternalEvent["host"].(string),
Matched: wrapped.InternalEvent["matched"].(string), Matched: wrapped.InternalEvent["matched"].(string),

View File

@ -24,7 +24,7 @@ func TestResponseToDSLMap(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile network request") require.Nil(t, err, "could not compile network request")
@ -49,7 +49,7 @@ func TestNetworkOperatorMatch(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile network request") require.Nil(t, err, "could not compile network request")
@ -112,7 +112,7 @@ func TestNetworkOperatorExtract(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile network request") require.Nil(t, err, "could not compile network request")
@ -175,7 +175,7 @@ func TestNetworkMakeResult(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile network request") require.Nil(t, err, "could not compile network request")

View File

@ -38,7 +38,7 @@ func TestNetworkExecuteWithResults(t *testing.T) {
} }
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID, ID: templateID,
Info: map[string]string{"severity": "low", "name": "test"}, Info: map[string]interface{}{"severity": "low", "name": "test"},
}) })
err := request.Compile(executerOpts) err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile network request") require.Nil(t, err, "could not compile network request")
@ -90,8 +90,4 @@ func TestNetworkExecuteWithResults(t *testing.T) {
require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results") require.Equal(t, "test", finalEvent.Results[0].MatcherName, "could not get correct matcher name of results")
require.Equal(t, 1, len(finalEvent.Results[0].ExtractedResults), "could not get correct number of extracted results") require.Equal(t, 1, len(finalEvent.Results[0].ExtractedResults), "could not get correct number of extracted results")
require.Equal(t, "<h1>400 - Bad Request</h1>", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results") require.Equal(t, "<h1>400 - Bad Request</h1>", finalEvent.Results[0].ExtractedResults[0], "could not get correct extracted results")
finalEvent = nil
request.Inputs[0].Type = ""
request.Inputs[0].Data = "GET / HTTP/1.1\r\n\r\n"
} }

View File

@ -30,7 +30,7 @@ type ExecuterOptions struct {
// TemplatePath is the path of the template for the request // TemplatePath is the path of the template for the request
TemplatePath string TemplatePath string
// TemplateInfo contains information block of the template request // TemplateInfo contains information block of the template request
TemplateInfo map[string]string TemplateInfo map[string]interface{}
// Output is a writer interface for writing output events from executer. // Output is a writer interface for writing output events from executer.
Output output.Writer Output output.Writer
// Options contains configuration options for the executer. // Options contains configuration options for the executer.

View File

@ -3,10 +3,12 @@ package templates
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols" "github.com/projectdiscovery/nuclei/v2/pkg/protocols"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/executer" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/executer"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
"github.com/projectdiscovery/nuclei/v2/pkg/workflows" "github.com/projectdiscovery/nuclei/v2/pkg/workflows"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -26,6 +28,21 @@ func Parse(filePath string, options *protocols.ExecuterOptions) (*Template, erro
} }
defer f.Close() defer f.Close()
if _, ok := template.Info["name"]; !ok {
return nil, errors.New("no template name field provided")
}
if _, ok := template.Info["author"]; !ok {
return nil, errors.New("no template author field provided")
}
if _, ok := template.Info["severity"]; !ok {
return nil, errors.New("no template severity field provided")
}
if templateTags, ok := template.Info["tags"]; ok && len(options.Options.Tags) > 0 {
if err := matchTemplateWithTags(templateTags.([]interface{}), options.Options); err != nil {
return nil, err
}
}
// Setting up variables regarding template metadata // Setting up variables regarding template metadata
options.TemplateID = template.ID options.TemplateID = template.ID
options.TemplateInfo = template.Info options.TemplateInfo = template.Info
@ -140,3 +157,40 @@ func (t *Template) parseWorkflowTemplate(workflow *workflows.WorkflowTemplate, o
} }
return nil return nil
} }
// matchTemplateWithTags matches if the template matches a tag
func matchTemplateWithTags(tags []interface{}, options *types.Options) error {
matched := false
mainLoop:
for _, tag := range options.Tags {
key, value := getKeyValue(tag)
for _, templTag := range tags {
tKey, tValue := getKeyValue(templTag.(string))
if strings.EqualFold(key, tKey) && strings.EqualFold(value, tValue) {
matched = true
break mainLoop
}
}
}
if !matched {
return errors.New("could not match template tags with input")
}
return nil
}
// getKeyValue returns key value pair for a data string
func getKeyValue(data string) (string, string) {
var key, value string
if strings.Contains(data, ":") {
parts := strings.SplitN(data, ":", 1)
if len(parts) > 2 {
key, value = parts[0], parts[1]
}
}
if value == "" {
value = data
}
return key, value
}

View File

@ -0,0 +1,25 @@
package templates
import (
"testing"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
"github.com/stretchr/testify/require"
)
func TestMatchTemplateWithTags(t *testing.T) {
templateTags := []interface{}{"php", "linux", "symfony"}
err := matchTemplateWithTags(templateTags, &types.Options{Tags: []string{"php"}})
require.Nil(t, err, "could not get php tag from input slice")
templateTags = []interface{}{"lang:php", "os:linux", "cms:symfony"}
err = matchTemplateWithTags(templateTags, &types.Options{Tags: []string{"cms:symfony"}})
require.Nil(t, err, "could not get php tag from input key value")
templateTags = []interface{}{"lang:php", "os:linux", "symfony"}
err = matchTemplateWithTags(templateTags, &types.Options{Tags: []string{"cms:symfony"}})
require.NotNil(t, err, "could get key value tag from input key value")
}

View File

@ -14,7 +14,7 @@ type Template struct {
// ID is the unique id for the template // ID is the unique id for the template
ID string `yaml:"id"` ID string `yaml:"id"`
// Info contains information about the template // Info contains information about the template
Info map[string]string `yaml:"info"` Info map[string]interface{} `yaml:"info"`
// RequestsHTTP contains the http request to make in the template // RequestsHTTP contains the http request to make in the template
RequestsHTTP []*http.Request `yaml:"requests,omitempty"` RequestsHTTP []*http.Request `yaml:"requests,omitempty"`
// RequestsDNS contains the dns request to make in the template // RequestsDNS contains the dns request to make in the template

View File

@ -80,4 +80,8 @@ type Options struct {
ExcludedTemplates goflags.StringSlice ExcludedTemplates goflags.StringSlice
// CustomHeaders is the list of custom global headers to send with each request. // CustomHeaders is the list of custom global headers to send with each request.
CustomHeaders goflags.StringSlice CustomHeaders goflags.StringSlice
// Tags contains a list of tags to execute templates for. Multiple paths
// can be specified with -l flag and -tags can be used in combination with
// the -l flag.
Tags goflags.StringSlice
} }