diff --git a/SYNTAX-REFERENCE.md b/SYNTAX-REFERENCE.md index 8ea798291..ed19bd9c0 100755 --- a/SYNTAX-REFERENCE.md +++ b/SYNTAX-REFERENCE.md @@ -880,7 +880,7 @@ Valid values:
-method string +method HTTPMethodTypeHolder
diff --git a/nuclei-jsonschema.json b/nuclei-jsonschema.json index da66f3e9b..1d179d68a 100755 --- a/nuclei-jsonschema.json +++ b/nuclei-jsonschema.json @@ -576,6 +576,23 @@ "additionalProperties": false, "type": "object" }, + "http.HTTPMethodTypeHolder": { + "enum": [ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "CONNECT", + "OPTIONS", + "TRACE", + "PATCH", + "PURGE" + ], + "type": "string", + "title": "method is the http request method", + "description": "Method is the HTTP Request Method,enum=GET,enum=HEAD,enum=POST,enum=PUT,enum=DELETE,enum=CONNECT,enum=OPTIONS,enum=TRACE,enum=PATCH,enum=PURGE" + }, "http.Request": { "properties": { "matchers": { @@ -637,19 +654,8 @@ "description": "Attack is the type of payload combinations to perform" }, "method": { - "enum": [ - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "CONNECT", - "OPTIONS", - "TRACE", - "PATCH", - "PURGE" - ], - "type": "string", + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/http.HTTPMethodTypeHolder", "title": "method is the http request method", "description": "Method is the HTTP Request Method" }, diff --git a/v2/pkg/protocols/http/build_request.go b/v2/pkg/protocols/http/build_request.go index 6b9b01f18..8c8a2dbc8 100644 --- a/v2/pkg/protocols/http/build_request.go +++ b/v2/pkg/protocols/http/build_request.go @@ -191,7 +191,7 @@ func (r *requestGenerator) makeHTTPRequestFromModel(ctx context.Context, data st return nil, errors.Wrap(err, "could not evaluate helper expressions") } - method, err := expressions.Evaluate(r.request.Method, finalValues) + method, err := expressions.Evaluate(r.request.Method.String(), finalValues) if err != nil { return nil, errors.Wrap(err, "could not evaluate helper expressions") } diff --git a/v2/pkg/protocols/http/build_request_test.go b/v2/pkg/protocols/http/build_request_test.go index 429676a37..a77e3c947 100644 --- a/v2/pkg/protocols/http/build_request_test.go +++ b/v2/pkg/protocols/http/build_request_test.go @@ -71,7 +71,7 @@ func TestMakeRequestFromModal(t *testing.T) { ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}/login.php"}, - Method: "POST", + Method: HTTPMethodTypeHolder{MethodType: HTTPPost}, Body: "username=test&password=pass", Headers: map[string]string{ "Content-Type": "application/x-www-form-urlencoded", @@ -103,7 +103,7 @@ func TestMakeRequestFromModalTrimSuffixSlash(t *testing.T) { ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?query=example"}, - Method: "GET", + Method: HTTPMethodTypeHolder{MethodType: HTTPGet}, } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, @@ -211,7 +211,7 @@ func TestMakeRequestFromModelUniqueInteractsh(t *testing.T) { ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}/?u=http://{{interactsh-url}}/&href=http://{{interactsh-url}}/&action=http://{{interactsh-url}}/&host={{interactsh-url}}"}, - Method: "GET", + Method: HTTPMethodTypeHolder{MethodType: HTTPGet}, } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, diff --git a/v2/pkg/protocols/http/cluster_test.go b/v2/pkg/protocols/http/cluster_test.go index 136b3feb9..a16db41ee 100644 --- a/v2/pkg/protocols/http/cluster_test.go +++ b/v2/pkg/protocols/http/cluster_test.go @@ -10,6 +10,6 @@ func TestCanCluster(t *testing.T) { req := &Request{Unsafe: true} require.False(t, req.CanCluster(&Request{}), "could cluster unsafe request") - req = &Request{Path: []string{"{{BaseURL}}"}, Method: "GET"} - require.True(t, req.CanCluster(&Request{Path: []string{"{{BaseURL}}"}, Method: "GET"}), "could not cluster GET request") + req = &Request{Path: []string{"{{BaseURL}}"}, Method: HTTPMethodTypeHolder{MethodType: HTTPGet}} + require.True(t, req.CanCluster(&Request{Path: []string{"{{BaseURL}}"}, Method: HTTPMethodTypeHolder{MethodType: HTTPGet}}), "could not cluster GET request") } diff --git a/v2/pkg/protocols/http/http.go b/v2/pkg/protocols/http/http.go index f85e49c2c..3fbe3a8ec 100644 --- a/v2/pkg/protocols/http/http.go +++ b/v2/pkg/protocols/http/http.go @@ -66,7 +66,7 @@ type Request struct { // - "TRACE" // - "PATCH" // - "PURGE" - Method string `yaml:"method,omitempty" jsonschema:"title=method is the http request method,description=Method is the HTTP Request Method,enum=GET,enum=HEAD,enum=POST,enum=PUT,enum=DELETE,enum=CONNECT,enum=OPTIONS,enum=TRACE,enum=PATCH,enum=PURGE"` + Method HTTPMethodTypeHolder `yaml:"method,omitempty" jsonschema:"title=method is the http request method,description=Method is the HTTP Request Method,enum=GET,enum=HEAD,enum=POST,enum=PUT,enum=DELETE,enum=CONNECT,enum=OPTIONS,enum=TRACE,enum=PATCH,enum=PURGE"` // description: | // Body is an optional parameter which contains HTTP Request body. // examples: @@ -242,7 +242,7 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error { var hasPayloadName bool // search for markers in all request parts var inputs []string - inputs = append(inputs, request.Method, request.Body) + inputs = append(inputs, request.Method.String(), request.Body) inputs = append(inputs, request.Raw...) for k, v := range request.customHeaders { inputs = append(inputs, fmt.Sprintf("%s: %s", k, v)) diff --git a/v2/pkg/protocols/http/http_method_types.go b/v2/pkg/protocols/http/http_method_types.go new file mode 100644 index 000000000..987dde979 --- /dev/null +++ b/v2/pkg/protocols/http/http_method_types.go @@ -0,0 +1,112 @@ +package http + +import ( + "encoding/json" + "errors" + "strings" + + "github.com/alecthomas/jsonschema" +) + +// HTTPMethodType is the type of the method specified +type HTTPMethodType int + +const ( + HTTPGet HTTPMethodType = iota + 1 + HTTPHead + HTTPPost + HTTPPut + HTTPDelete + HTTPConnect + HTTPOptions + HTTPTrace + HTTPPatch + HTTPPurge + //limit + limit +) + +// HTTPMethodMapping is a table for conversion of method from string. +var HTTPMethodMapping = map[HTTPMethodType]string{ + HTTPGet: "GET", + HTTPHead: "HEAD", + HTTPPost: "POST", + HTTPPut: "PUT", + HTTPDelete: "DELETE", + HTTPConnect: "CONNECT", + HTTPOptions: "OPTIONS", + HTTPTrace: "TRACE", + HTTPPatch: "PATCH", + HTTPPurge: "PURGE", +} + +// GetSupportedHTTPMethodTypes returns list of supported types +func GetSupportedHTTPMethodTypes() []HTTPMethodType { + var result []HTTPMethodType + for index := HTTPMethodType(1); index < limit; index++ { + result = append(result, index) + } + return result +} + +func toHTTPMethodTypes(valueToMap string) (HTTPMethodType, error) { + normalizedValue := normalizeValue(valueToMap) + for key, currentValue := range HTTPMethodMapping { + if normalizedValue == currentValue { + return key, nil + } + } + return -1, errors.New("Invalid HTTP method verb: " + valueToMap) +} + +func normalizeValue(value string) string { + return strings.TrimSpace(strings.ToUpper(value)) +} + +func (t HTTPMethodType) String() string { + return HTTPMethodMapping[t] +} + +// HTTPMethodTypeHolder is used to hold internal type of the HTTP Method +type HTTPMethodTypeHolder struct { + MethodType HTTPMethodType +} + +func (holder HTTPMethodTypeHolder) String() string { + return holder.MethodType.String() +} + +func (holder HTTPMethodTypeHolder) JSONSchemaType() *jsonschema.Type { + gotType := &jsonschema.Type{ + Type: "string", + Title: "method is the HTTP request method", + Description: "Method is the HTTP Request Method,enum=GET,enum=HEAD,enum=POST,enum=PUT,enum=DELETE,enum=CONNECT,enum=OPTIONS,enum=TRACE,enum=PATCH,enum=PURGE", + } + for _, types := range GetSupportedHTTPMethodTypes() { + gotType.Enum = append(gotType.Enum, types.String()) + } + return gotType +} + +func (holder *HTTPMethodTypeHolder) UnmarshalYAML(unmarshal func(interface{}) error) error { + var marshalledTypes string + if err := unmarshal(&marshalledTypes); err != nil { + return err + } + + computedType, err := toHTTPMethodTypes(marshalledTypes) + if err != nil { + return err + } + + holder.MethodType = computedType + return nil +} + +func (holder *HTTPMethodTypeHolder) MarshalJSON() ([]byte, error) { + return json.Marshal(holder.MethodType.String()) +} + +func (holder HTTPMethodTypeHolder) MarshalYAML() (interface{}, error) { + return holder.MethodType.String(), nil +} diff --git a/v2/pkg/protocols/http/operators_test.go b/v2/pkg/protocols/http/operators_test.go index bc7b888a2..7950bc64f 100644 --- a/v2/pkg/protocols/http/operators_test.go +++ b/v2/pkg/protocols/http/operators_test.go @@ -25,7 +25,7 @@ func TestResponseToDSLMap(t *testing.T) { ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, - Method: "GET", + Method: HTTPMethodTypeHolder{MethodType: HTTPGet}, } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, @@ -55,7 +55,7 @@ func TestHTTPOperatorMatch(t *testing.T) { ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, - Method: "GET", + Method: HTTPMethodTypeHolder{MethodType: HTTPGet}, } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, @@ -143,7 +143,7 @@ func TestHTTPOperatorExtract(t *testing.T) { ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, - Method: "GET", + Method: HTTPMethodTypeHolder{MethodType: HTTPGet}, } executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{ ID: templateID, @@ -257,7 +257,7 @@ func TestHTTPMakeResult(t *testing.T) { ID: templateID, Name: "testing", Path: []string{"{{BaseURL}}?test=1"}, - Method: "GET", + Method: HTTPMethodTypeHolder{MethodType: HTTPGet}, Operators: operators.Operators{ Matchers: []*matchers.Matcher{{ Name: "test", diff --git a/v2/pkg/templates/templates_doc.go b/v2/pkg/templates/templates_doc.go index c7f1b016b..52c5da75a 100644 --- a/v2/pkg/templates/templates_doc.go +++ b/v2/pkg/templates/templates_doc.go @@ -344,7 +344,7 @@ func init() { "clusterbomb", } HTTPRequestDoc.Fields[8].Name = "method" - HTTPRequestDoc.Fields[8].Type = "string" + HTTPRequestDoc.Fields[8].Type = "HTTPMethodTypeHolder" HTTPRequestDoc.Fields[8].Note = "" HTTPRequestDoc.Fields[8].Description = "Method is the HTTP Request Method." HTTPRequestDoc.Fields[8].Comments[encoder.LineComment] = "Method is the HTTP Request Method." diff --git a/v2/pkg/templates/templates_doc_examples.go b/v2/pkg/templates/templates_doc_examples.go index e6655c6d9..88d5e5e88 100644 --- a/v2/pkg/templates/templates_doc_examples.go +++ b/v2/pkg/templates/templates_doc_examples.go @@ -24,7 +24,7 @@ var ( Tags: stringslice.StringSlice{Value: "cve,cve2021,rce,ruby"}, } exampleNormalHTTPRequest = &http.Request{ - Method: "GET", + Method: http.HTTPMethodTypeHolder{MethodType: http.HTTPGet}, Path: []string{"{{BaseURL}}/.git/config"}, Operators: operators.Operators{ MatchersCondition: "and",