mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 10:10:18 -10:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7658d9079a | ||
|
|
1ae72d440f | ||
|
|
8b94feb3bc | ||
|
|
72f5e607b1 |
No files matched your search
+1
-1
@@ -35,4 +35,4 @@ pkg/protocols/headless/engine/.cache
|
||||
**/*-cache
|
||||
/fuzzplayground
|
||||
integration_tests/fuzzplayground
|
||||
|
||||
/docgen
|
||||
@@ -47,4 +47,8 @@ fuzzplayground:
|
||||
memogen:
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "memogen" cmd/memogen/memogen.go
|
||||
./memogen -src pkg/js/libs -tpl cmd/memogen/function.tpl
|
||||
genschema:
|
||||
rm -f nuclei-jsonschema.json docgen 2>/dev/null
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "docgen" cmd/docgen/docgen.go
|
||||
./docgen /dev/null nuclei-jsonschema.json
|
||||
|
||||
+32
-1
@@ -32,7 +32,9 @@ func main() {
|
||||
}
|
||||
|
||||
// Generate jsonschema
|
||||
r := &jsonschema.Reflector{}
|
||||
r := &jsonschema.Reflector{
|
||||
BaseSchemaID: jsonschema.ID("https://nuclei.projectdiscovery.io/"),
|
||||
}
|
||||
jsonschemaData := r.Reflect(&templates.Template{})
|
||||
|
||||
var buf bytes.Buffer
|
||||
@@ -44,8 +46,37 @@ func main() {
|
||||
for _, match := range pathRegex.FindAllStringSubmatch(schema, -1) {
|
||||
schema = strings.ReplaceAll(schema, match[0], match[1])
|
||||
}
|
||||
var m map[string]interface{}
|
||||
err = json.Unmarshal([]byte(schema), &m)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not unmarshal jsonschema: %s\n", err)
|
||||
}
|
||||
|
||||
// patch the schema to enable markdown Descriptions in monaco and vscode
|
||||
updateDescriptionKeyName("", m)
|
||||
|
||||
schemax, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not marshal jsonschema: %s\n", err)
|
||||
}
|
||||
schema = string(schemax)
|
||||
|
||||
err = os.WriteFile(os.Args[2], []byte(schema), 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not write jsonschema: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// will recursively find and replace/rename PropName in description
|
||||
func updateDescriptionKeyName(parent string, m map[string]interface{}) {
|
||||
for k, v := range m {
|
||||
if k == "description" && parent != "properties" {
|
||||
delete(m, k)
|
||||
m["markdownDescription"] = v
|
||||
}
|
||||
// if v is of type object then recursively call this function
|
||||
if vMap, ok := v.(map[string]interface{}); ok {
|
||||
updateDescriptionKeyName(k, vMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
+992
-675
File diff suppressed because it is too large.
Load diff
+3
-30
@@ -4,19 +4,9 @@ import (
|
||||
"github.com/invopop/jsonschema"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/schema"
|
||||
)
|
||||
|
||||
type schemaMetadata struct {
|
||||
PropName string
|
||||
PropType string
|
||||
Example []interface{}
|
||||
OneOf []*schemaMetadata
|
||||
}
|
||||
|
||||
var infoSchemaMetadata = []schemaMetadata{
|
||||
{PropName: "author", OneOf: []*schemaMetadata{{PropType: "string", Example: []interface{}{`pdteam`}}, {PropType: "array", Example: []interface{}{`pdteam,mr.robot`}}}},
|
||||
}
|
||||
|
||||
// Info contains metadata information about a template
|
||||
type Info struct {
|
||||
// description: |
|
||||
@@ -96,25 +86,8 @@ type Info struct {
|
||||
|
||||
// JSONSchemaProperty returns the JSON schema property for the Info object.
|
||||
func (i Info) JSONSchemaExtend(base *jsonschema.Schema) {
|
||||
// since we are re-using a stringslice and rawStringSlice everywhere, we can extend/edit the schema here
|
||||
// thus allowing us to add examples, descriptions, etc. to the properties
|
||||
for _, metadata := range infoSchemaMetadata {
|
||||
if prop, ok := base.Properties.Get(metadata.PropName); ok {
|
||||
if len(metadata.OneOf) > 0 {
|
||||
for _, oneOf := range metadata.OneOf {
|
||||
prop.OneOf = append(prop.OneOf, &jsonschema.Schema{
|
||||
Type: oneOf.PropType,
|
||||
Examples: oneOf.Example,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if metadata.PropType != "" {
|
||||
prop.Type = metadata.PropType
|
||||
}
|
||||
prop.Examples = []interface{}{metadata.Example}
|
||||
}
|
||||
}
|
||||
}
|
||||
base.Examples = infoSectionExamples
|
||||
schema.ExtendSchema(infoSectionMetadata, base)
|
||||
}
|
||||
|
||||
// Classification contains the vulnerability classification data for a template.
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package model
|
||||
|
||||
import "github.com/projectdiscovery/nuclei/v3/pkg/utils/schema"
|
||||
|
||||
// infoSectionMetadata contains all metadata information about Info struct
|
||||
var infoSectionMetadata = []schema.PropertyMetadata{
|
||||
{
|
||||
PropName: "name",
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Name should be good short summary that identifies what the template does",
|
||||
),
|
||||
Example: schema.PropertyExamples("example-template", "TeamCity - Authentication Bypass"),
|
||||
},
|
||||
{
|
||||
PropName: "author",
|
||||
RemoveRef: true,
|
||||
OneOf: []*schema.PropertyMetadata{
|
||||
{
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Author of the template without any special characters",
|
||||
"It can be a single author or comma-separated multiple authors",
|
||||
),
|
||||
Example: schema.PropertyExample("pdteam"),
|
||||
},
|
||||
{
|
||||
PropType: "array",
|
||||
Description: schema.MultiLine(
|
||||
"Author of the template without any special characters",
|
||||
"It can be a single author or comma-separated multiple authors",
|
||||
),
|
||||
Example: schema.PropertyExample("pdteam,mr.robot"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
PropName: "tags",
|
||||
RemoveRef: true,
|
||||
OneOf: []*schema.PropertyMetadata{
|
||||
{
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Tags for the template separated by commas (No spaces)",
|
||||
),
|
||||
Example: schema.PropertyExample("cve"),
|
||||
},
|
||||
{
|
||||
PropType: "array",
|
||||
Description: schema.MultiLine(
|
||||
"Multiple tags for the template separated by commas (No spaces)",
|
||||
),
|
||||
Example: schema.PropertyExamples("cve,http", "http,oast", "cve2024,cve"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
PropName: "description",
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Description of the template.",
|
||||
"You can go in-depth here on what the template actually does.",
|
||||
"Example:",
|
||||
"Apache Log4j2 <=2.14.1 JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled.",
|
||||
),
|
||||
Example: schema.PropertyExample("Example description of the template"),
|
||||
},
|
||||
{
|
||||
PropName: "impact",
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Impact of the template.",
|
||||
"You can go in-depth here on impact of the template.",
|
||||
"Example:",
|
||||
"Successful exploitation of this vulnerability can lead to remote code execution, potentially compromising the affected system.",
|
||||
),
|
||||
Example: schema.PropertyExample("Impact of the template"),
|
||||
},
|
||||
{
|
||||
PropName: "reference",
|
||||
PropType: "array",
|
||||
Description: schema.MultiLine(
|
||||
"References for the template.",
|
||||
"This should contain links relevant to the template.",
|
||||
"Example:",
|
||||
"https://logging.apache.org/log4j/2.x/security.html",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2021-44228",
|
||||
),
|
||||
Example: schema.PropertyExample("\n\t- https://example.com"),
|
||||
},
|
||||
{
|
||||
PropName: "severity",
|
||||
Description: schema.MultiLine(
|
||||
"Severity of the template.",
|
||||
"supported values: info, low, medium, high, critical, unknown",
|
||||
),
|
||||
Example: schema.PropertyExample("info"),
|
||||
},
|
||||
{
|
||||
PropName: "metadata",
|
||||
Description: schema.MultiLine(
|
||||
"Optional metadata for the template.",
|
||||
"this could be any useful metadata like Vendor, Product, Version, etc.",
|
||||
"Note - This is autogenerated and updated regularly for templates merged in projectdiscovery/nuclei-templates repository.",
|
||||
"Example:",
|
||||
"```yaml",
|
||||
"Vendor: Apache",
|
||||
"Product: Log4j2",
|
||||
"Version: <=2.14.1",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
"\n\tmax-request: 1",
|
||||
),
|
||||
schema.MultiLine(
|
||||
"\n\tmax-request: 2",
|
||||
"\tvendor: apache",
|
||||
"\tproduct: log4j2",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "classification",
|
||||
Description: schema.MultiLine(
|
||||
"Classification contains classification information about the template.",
|
||||
"This could be any classification information like CWE, CVE, EPSS etc.",
|
||||
"Note - This is autogenerated and updated regularly for CVE templates merged in projectdiscovery/nuclei-templates repository.",
|
||||
"Example:",
|
||||
"```yaml",
|
||||
"cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
|
||||
"cvss-score: 10",
|
||||
"cve-id: CVE-2021-44228",
|
||||
"cwe-id: CWE-20,CWE-917",
|
||||
"epss-score: 0.97453",
|
||||
"epss-percentile: 0.99942",
|
||||
"cpe: cpe:2.3:a:apache:log4j:*:*:*:*:*:*:*:*",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
"\n\tcvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
|
||||
"\tcvss-score: 10",
|
||||
"\tcve-id: CVE-2021-44228",
|
||||
),
|
||||
schema.MultiLine(
|
||||
"\n\tcwe-id: CWE-20,CWE-917",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "Remediation",
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Remediation steps for the template.",
|
||||
"You can go in-depth here on how to mitigate the problem found by this template.",
|
||||
"Example:",
|
||||
"Upgrade to Log4j 2.3.1 (for Java 6), 2.12.3 (for Java 7), or 2.17.0 (for Java 8 and later).",
|
||||
),
|
||||
Example: schema.PropertyExample("Remediation steps for the template"),
|
||||
},
|
||||
}
|
||||
|
||||
// infoSectionExamples contains examples for Info struct
|
||||
var infoSectionExamples = schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
"\n\tname: Example Template",
|
||||
"\tauthor: pdteam",
|
||||
"\tseverity: info",
|
||||
"\tdescription: This is an example template",
|
||||
),
|
||||
schema.MultiLine(
|
||||
"\n\tname: example-template",
|
||||
"\tauthor: pdteam",
|
||||
"\tseverity: unknown",
|
||||
"\tdescription: Example description of the template",
|
||||
"\timpact: Impact of the template",
|
||||
"\treference: ",
|
||||
"\t - https://example.com",
|
||||
"\tmetadata: ",
|
||||
"\t max-request: 1",
|
||||
"\ttags: http",
|
||||
),
|
||||
)
|
||||
@@ -61,16 +61,17 @@ type AttackTypeHolder struct {
|
||||
Value AttackType `mapping:"true"`
|
||||
}
|
||||
|
||||
func (holder AttackTypeHolder) JSONSchemaType() *jsonschema.Schema {
|
||||
gotType := &jsonschema.Schema{
|
||||
func (AttackTypeHolder) JSONSchema() *jsonschema.Schema {
|
||||
enums := []interface{}{}
|
||||
for _, severity := range GetSupportedAttackTypes() {
|
||||
enums = append(enums, severity.String())
|
||||
}
|
||||
return &jsonschema.Schema{
|
||||
Title: "attack is the payload combination",
|
||||
Description: "Attack is the type of payload combinations to perform",
|
||||
Type: "string",
|
||||
Title: "type of the attack",
|
||||
Description: "Type of the attack",
|
||||
Enum: enums,
|
||||
}
|
||||
for _, types := range GetSupportedAttackTypes() {
|
||||
gotType.Enum = append(gotType.Enum, types.String())
|
||||
}
|
||||
return gotType
|
||||
}
|
||||
|
||||
func (holder *AttackTypeHolder) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/invopop/jsonschema"
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
|
||||
httputil "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils/http"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/schema"
|
||||
"github.com/projectdiscovery/rawhttp"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
@@ -58,10 +60,10 @@ type Request struct {
|
||||
// - "batteringram"
|
||||
// - "pitchfork"
|
||||
// - "clusterbomb"
|
||||
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
|
||||
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty"`
|
||||
// description: |
|
||||
// Method is the HTTP Request Method.
|
||||
Method HTTPMethodTypeHolder `yaml:"method,omitempty" json:"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" json:"method,omitempty"`
|
||||
// description: |
|
||||
// Body is an optional parameter which contains HTTP Request body.
|
||||
// examples:
|
||||
@@ -74,14 +76,14 @@ type Request struct {
|
||||
// Payloads support both key-values combinations where a list
|
||||
// of payloads is provided, or optionally a single file can also
|
||||
// be provided as payload which will be read on run-time.
|
||||
Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the http request,description=Payloads contains any payloads for the current request"`
|
||||
Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"type=object,title=payloads for the http request,description=Payloads contains any payloads for the current request"`
|
||||
|
||||
// description: |
|
||||
// Headers contains HTTP Headers to send with the request.
|
||||
// examples:
|
||||
// - value: |
|
||||
// map[string]string{"Content-Type": "application/x-www-form-urlencoded", "Content-Length": "1", "Any-Header": "Any-Value"}
|
||||
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty" jsonschema:"title=headers to send with the http request,description=Headers contains HTTP Headers to send with the request"`
|
||||
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty" jsonschema:"type=object,title=headers to send with the http request,description=Headers contains HTTP Headers to send with the request"`
|
||||
// description: |
|
||||
// RaceCount is the number of times to send a request in Race Condition Attack.
|
||||
// examples:
|
||||
@@ -220,6 +222,12 @@ type Request struct {
|
||||
fuzzingFilterCondition matchers.ConditionType `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
// JSONSchemaExtend extends json schema definations from struct
|
||||
func (Request) JSONSchemaExtend(base *jsonschema.Schema) {
|
||||
schema.ExtendSchema(httpRequestMetadata, base)
|
||||
schema.ApplyAnyOfRequired(httpRequestAnyOfRequired, base)
|
||||
}
|
||||
|
||||
// Options returns executer options for http request
|
||||
func (r *Request) Options() *protocols.ExecutorOptions {
|
||||
return r.options
|
||||
|
||||
@@ -62,6 +62,14 @@ func GetSupportedHTTPMethodTypes() []HTTPMethodType {
|
||||
return result
|
||||
}
|
||||
|
||||
func GetHTTPMethods() []interface{} {
|
||||
var result []interface{}
|
||||
for _, method := range GetSupportedHTTPMethodTypes() {
|
||||
result = append(result, method.String())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func toHTTPMethodTypes(valueToMap string) (HTTPMethodType, error) {
|
||||
normalizedValue := normalizeValue(valueToMap)
|
||||
for key, currentValue := range HTTPMethodMapping {
|
||||
@@ -89,16 +97,17 @@ func (holder HTTPMethodTypeHolder) String() string {
|
||||
return holder.MethodType.String()
|
||||
}
|
||||
|
||||
func (holder HTTPMethodTypeHolder) JSONSchemaType() *jsonschema.Schema {
|
||||
gotType := &jsonschema.Schema{
|
||||
Type: "string",
|
||||
func (HTTPMethodTypeHolder) JSONSchema() *jsonschema.Schema {
|
||||
enums := []interface{}{}
|
||||
for _, severity := range GetSupportedHTTPMethodTypes() {
|
||||
enums = append(enums, severity.String())
|
||||
}
|
||||
return &jsonschema.Schema{
|
||||
Title: "method is the HTTP request method",
|
||||
Description: "Method is the HTTP Request Method",
|
||||
Type: "string",
|
||||
Enum: enums,
|
||||
}
|
||||
for _, types := range GetSupportedHTTPMethodTypes() {
|
||||
gotType.Enum = append(gotType.Enum, types.String())
|
||||
}
|
||||
return gotType
|
||||
}
|
||||
|
||||
func (holder *HTTPMethodTypeHolder) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
|
||||
@@ -0,0 +1,508 @@
|
||||
package http
|
||||
|
||||
import "github.com/projectdiscovery/nuclei/v3/pkg/utils/schema"
|
||||
|
||||
// httpRequestMetadata / properties
|
||||
var httpRequestMetadata = []schema.PropertyMetadata{
|
||||
{
|
||||
PropName: "id",
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Optional ID of this HTTP Request Block",
|
||||
"This is useful when calling /executing this request block in flow",
|
||||
"By default, current index in the http array is used as ID",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- id: wp-login",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample("example-http-id"),
|
||||
},
|
||||
{
|
||||
PropName: "path",
|
||||
Description: schema.MultiLine(
|
||||
"Path contains HTTP URL[s] to be sent. Here only Path portion is generally specified and host is templatetized",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- path:",
|
||||
"\t"+`{{BaseURL}}/admin`,
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
"\n\t- '{{BaseURL}}'",
|
||||
"\n\t- '{{RootURL}}'",
|
||||
"\n\t- '{{Hostname}}:8080/ca-cert'",
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "raw",
|
||||
Description: schema.MultiLine(
|
||||
"Raw contains HTTP Request[s] in raw or request dump format",
|
||||
"Example: ",
|
||||
schema.MultiLine(
|
||||
"- raw:",
|
||||
"\t- |",
|
||||
"\t\tGET / HTTP/1.1",
|
||||
"\t\tHost: {{Hostname}}",
|
||||
"\n",
|
||||
),
|
||||
"\nNote: If input contains any path/query-params they are automerged unless explicitly disabled using 'disable-path-automerge'",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
"\n# raw-get-request",
|
||||
"\t- |",
|
||||
"\t\tGET / HTTP/1.1",
|
||||
"\t\tHost: {{Hostname}}",
|
||||
"\n",
|
||||
),
|
||||
schema.MultiLine(
|
||||
"\n# raw-post-request",
|
||||
"\t- |",
|
||||
"\t\tPOST / HTTP/1.1",
|
||||
"\t\tHost: {{Hostname}}",
|
||||
"\t\tContent-Type: application/json",
|
||||
"\t\tContent-Length: 13",
|
||||
"\n",
|
||||
"\t\t{\"key\": \"value\"}",
|
||||
"\n",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "name",
|
||||
Description: schema.MultiLine(
|
||||
"Name is the optional name of the request",
|
||||
"If a name is specified, all the named request in a template can be matched upon",
|
||||
"in a combined manner allowing multi-request based matchers.",
|
||||
),
|
||||
Example: schema.PropertyExample("example-http-name"),
|
||||
},
|
||||
{
|
||||
PropName: "method",
|
||||
Description: schema.MultiLine(
|
||||
"Method is the HTTP method to be used for the request",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- method: GET",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(GetHTTPMethods()...),
|
||||
},
|
||||
{
|
||||
PropName: "headers",
|
||||
Description: schema.MultiLine(
|
||||
"Headers contains the HTTP headers to be sent with the request",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- headers:",
|
||||
"\tUser-Agent: Mozilla/5.0",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
"User-Agent: Mozilla/5.0",
|
||||
"Authorization: Bearer {{token}}",
|
||||
"X-Forwarded-For: {{Hostname}}",
|
||||
"Origin: {{BaseURL}}",
|
||||
"Referer: {{BaseURL}}/admin",
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "body",
|
||||
Description: schema.MultiLine(
|
||||
"Body contains the HTTP body to be sent with the request",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- body: \"key=value&key2=value2\"",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample("key=value&key2=value2"),
|
||||
},
|
||||
{
|
||||
PropName: "redirects",
|
||||
Description: schema.MultiLine(
|
||||
"Redirects specifies whether to follow redirects or not",
|
||||
"This can be use in conjunction with max-redirects to effectively control the redirects",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- redirects: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "host-redirects",
|
||||
Description: schema.MultiLine(
|
||||
"HostRedirects specifies whether only redirects to the same host should be followed by the HTTP Client",
|
||||
"When set to true, only redirects to the same host are followed",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- host-redirects: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "max-redirects",
|
||||
Description: schema.MultiLine(
|
||||
"MaxRedirects is the maximum number of redirects to follow",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- max-redirects: 10",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(10),
|
||||
},
|
||||
{
|
||||
PropName: "disable-path-automerge",
|
||||
Description: schema.MultiLine(
|
||||
"DisablePathAutomerge disables the automatic merging of path/query-params from input/target to path defined in template",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- disable-path-automerge: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "unsafe",
|
||||
Description: schema.MultiLine(
|
||||
"Unsafe specifies whether to use rawhttp engine for sending Non RFC-Compliant requests.",
|
||||
"This uses the [rawhttp](https://github.com/projectdiscovery/rawhttp) engine to achieve complete",
|
||||
"control over the request, with no normalization performed by the client.",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- unsafe: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "skip-variables-check",
|
||||
Description: schema.MultiLine(
|
||||
"SkipVariablesCheck skips the check for unresolved variables in request",
|
||||
"This is also useful in case where body contains payload in format of {{xyz}}",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- skip-variables-check: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "max-size",
|
||||
Description: schema.MultiLine(
|
||||
"MaxSize is the maximum size of the response body to be read",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- max-size: 1024",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(1024),
|
||||
},
|
||||
{
|
||||
PropName: "read-all",
|
||||
Description: schema.MultiLine(
|
||||
"Enables force reading of the entire raw unsafe request body ignoring any specified content length headers",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- read-all: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "self-contained",
|
||||
Description: schema.MultiLine(
|
||||
"SelfContained specifies whether the request is self-contained or not",
|
||||
"This means if the request is dependent on input or is self-contained",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- self-contained: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "cookie-reuse",
|
||||
Description: schema.MultiLine(
|
||||
"CookieReuse specifies whether to reuse the cookies from previous requests",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- cookie-reuse: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "disable-cookie",
|
||||
Description: schema.MultiLine(
|
||||
"Disable Cookie is an optional setting to disable cookie reuse for this request",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- disable-cookie: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "stop-at-first-match",
|
||||
Description: schema.MultiLine(
|
||||
"StopAtFirstMatch specifies whether to stop at first match or not",
|
||||
"This is useful for cases like brute-forcing where we want to stop once we find the first correct login credential",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- stop-at-first-match: true",
|
||||
"```",
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "digest-username",
|
||||
Description: schema.MultiLine(
|
||||
"DigestUsername specifies the username for digest authentication",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- digest-username: admin",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample("admin"),
|
||||
},
|
||||
{
|
||||
PropName: "digest-password",
|
||||
Description: schema.MultiLine(
|
||||
"DigestPassword specifies the password for digest authentication",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- digest-password: admin",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample("admin"),
|
||||
},
|
||||
{
|
||||
PropName: "filters",
|
||||
Description: schema.MultiLine(
|
||||
"Filter is only applicable for fuzzing requests and specifies whether this template should be run on this input or not",
|
||||
"It is a twin of matchers and it can be used to write any pre-conditions that should be met before running the template",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- filters:",
|
||||
"\t- type: dsl",
|
||||
"\t\tdsl:",
|
||||
"\t\t\t- 'len(query) > 0'",
|
||||
"```",
|
||||
"With this filter only those requests will be executed where query param is present in input will be executed by this template",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
" # has query params",
|
||||
"\t- type: dsl",
|
||||
"\t\tdsl:",
|
||||
"\t\t\t- 'len(query) > 0'",
|
||||
"\n",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # has request body",
|
||||
"\t- type: dsl",
|
||||
"\t\tdsl:",
|
||||
"\t\t\t- 'len(body) > 0'",
|
||||
"\n",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # has post method",
|
||||
"\t- type: dsl",
|
||||
"\t\tdsl:",
|
||||
"\t\t\t- 'method == \"POST\"'",
|
||||
"\n",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # has post method and body",
|
||||
"\t- type: dsl",
|
||||
"\t\tdsl:",
|
||||
"\t\t\t- 'method == \"POST\"'",
|
||||
"\t\t\t- 'len(body) > 0'",
|
||||
"\t\tcondition: and",
|
||||
"\n",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "filters-condition",
|
||||
Description: schema.MultiLine(
|
||||
"FiltersCondition specifies the condition to be used for combining multiple filters",
|
||||
"By default, it is OR",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- filters-condition: and",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample("and"),
|
||||
Default: "OR",
|
||||
},
|
||||
{
|
||||
PropName: "payloads",
|
||||
Description: schema.MultiLine(
|
||||
"Payloads contains key-value pairs of payloads to be used",
|
||||
"These payloads when referenced in http request will be iterated appropriately and replaced",
|
||||
"Optionally payload also support loading values from file instead of defining them inline",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- payloads:",
|
||||
"\tusername:",
|
||||
"\t\t- admin",
|
||||
"\t\t- root",
|
||||
"\tpassword:",
|
||||
"\t\t- password",
|
||||
"\t\t- admin",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
" # inline payloads",
|
||||
"\tusername:",
|
||||
"\t\t- admin",
|
||||
"\t\t- root",
|
||||
"\tpassword:",
|
||||
"\t\t- password",
|
||||
"\t\t- admin",
|
||||
"\n",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # file payloads",
|
||||
"\tusername: /path/to/usernames.txt",
|
||||
"\tpassword: /path/to/passwords.txt",
|
||||
"\n",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "attack",
|
||||
Description: schema.MultiLine(
|
||||
"Attack is the type of payload combinations to perform",
|
||||
"batteringram the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates permutations and combinations for all payloads",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- attack: clusterbomb",
|
||||
"```",
|
||||
"Note: this is only applicable when 'payloads' are defined",
|
||||
),
|
||||
Example: schema.PropertyExamples("batteringram", "pitchfork", "clusterbomb"),
|
||||
},
|
||||
{
|
||||
PropName: "threads",
|
||||
Description: schema.MultiLine(
|
||||
"Threads specifies concurreny with which requests should be sent",
|
||||
"It should not be specified unless explicitly behaviour and its value is obtained from -pc (-payload-concurrency) flag",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- threads: 10",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(10),
|
||||
},
|
||||
{
|
||||
PropName: "race",
|
||||
Description: schema.MultiLine(
|
||||
"Race determines if all the request have to be attempted at the same time (Race Condition)",
|
||||
"The actual number of requests that will be sent is determined by the `race_count` field.",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- race: true",
|
||||
" race_count: 100",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "race_count",
|
||||
Description: schema.MultiLine(
|
||||
"RaceCount specifies the number of requests to be sent when attempting race condition attacks",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- race: true",
|
||||
" race_count: 100",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(100),
|
||||
},
|
||||
{
|
||||
PropName: "pipeline",
|
||||
Description: schema.MultiLine(
|
||||
"Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining",
|
||||
"All requests must be idempotent (GET/POST). This can be used for race conditions/billions requests.",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- pipeline: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "pipeline-concurrent-connections",
|
||||
Description: schema.MultiLine(
|
||||
"PipelineConcurrentConnections specifies the number of concurrent connections to be used for pipelining",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- pipeline-concurrent-connections: 10",
|
||||
" pipeline: true",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(10),
|
||||
},
|
||||
{
|
||||
PropName: "pipeline-requests-per-connection",
|
||||
Description: schema.MultiLine(
|
||||
"PipelineRequestsPerConnection specifies the number of requests to be sent per connection",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"- pipeline: true",
|
||||
" pipeline-requests-per-connection: 10",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(10),
|
||||
},
|
||||
{
|
||||
PropName: "fuzzing",
|
||||
Description: schema.MultiLine(
|
||||
"Fuzzing or DAST specifies rules for fuzzing requests",
|
||||
"these rules mutate the input request to generate multiple requests",
|
||||
"For example, replacing all query param values with sqli payloads",
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "req-condition",
|
||||
Description: schema.MultiLine(
|
||||
"Deprecated: this is automatically identified and used (https://github.com/projectdiscovery/nuclei/issues/2393)",
|
||||
),
|
||||
Deprecated: true,
|
||||
},
|
||||
{
|
||||
PropName: "iterate-all",
|
||||
Description: schema.MultiLine(
|
||||
"Deprecated: use flow to properly specify iteration logic",
|
||||
),
|
||||
Deprecated: true,
|
||||
},
|
||||
{
|
||||
PropName: "cookie-reuse",
|
||||
Description: schema.MultiLine(
|
||||
"Deprecated: this is default behaviour now use disable-cookie to explicitly disable cookie reuse",
|
||||
),
|
||||
Deprecated: true,
|
||||
},
|
||||
{
|
||||
PropName: "signature",
|
||||
Deprecated: true,
|
||||
},
|
||||
}
|
||||
|
||||
// httpRequestAnyOf required combinations
|
||||
var httpRequestAnyOfRequired = []schema.RequiredCombos{
|
||||
schema.Require("path"),
|
||||
schema.Require("raw"),
|
||||
schema.Require("filters", "payload", "fuzzing"),
|
||||
schema.RequireBase(
|
||||
[]string{"payloads"},
|
||||
schema.Require("path"),
|
||||
schema.Require("raw"),
|
||||
),
|
||||
}
|
||||
@@ -51,16 +51,17 @@ type SignatureTypeHolder struct {
|
||||
Value SignatureType
|
||||
}
|
||||
|
||||
func (holder SignatureTypeHolder) JSONSchemaType() *jsonschema.Schema {
|
||||
gotType := &jsonschema.Schema{
|
||||
func (signature SignatureTypeHolder) JSONSchema() *jsonschema.Schema {
|
||||
enums := []interface{}{}
|
||||
for _, severity := range GetSupportedSignaturesTypes() {
|
||||
enums = append(enums, severity.String())
|
||||
}
|
||||
return &jsonschema.Schema{
|
||||
Type: "string",
|
||||
Title: "type of the signature",
|
||||
Description: "Type of the signature",
|
||||
Enum: enums,
|
||||
}
|
||||
for _, types := range GetSupportedSignaturesTypes() {
|
||||
gotType.Enum = append(gotType.Enum, types.String())
|
||||
}
|
||||
return gotType
|
||||
}
|
||||
|
||||
func (holder *SignatureTypeHolder) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
validate "github.com/go-playground/validator/v10"
|
||||
"github.com/invopop/jsonschema"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/code"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/websocket"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/whois"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/schema"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
@@ -45,7 +47,7 @@ type Template struct {
|
||||
// examples:
|
||||
// - name: ID Example
|
||||
// value: "\"CVE-2021-19520\""
|
||||
ID string `yaml:"id" json:"id" jsonschema:"title=id of the template,description=The Unique ID for the template,required,example=cve-2021-19520,pattern=^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$"`
|
||||
ID string `yaml:"id" json:"id" jsonschema:"title=id of the template,pattern=^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$,required"`
|
||||
// description: |
|
||||
// Info contains metadata information about the template.
|
||||
// examples:
|
||||
@@ -62,7 +64,7 @@ type Template struct {
|
||||
// http(1)
|
||||
// }
|
||||
//
|
||||
Flow string `yaml:"flow,omitempty" json:"flow,omitempty" jsonschema:"title=template execution flow in js,description=Flow contains js code which defines how the template should be executed,type=string,example='flow: http(0) && http(1)'"`
|
||||
Flow string `yaml:"flow,omitempty" json:"flow,omitempty" jsonschema:"title=template execution flow in js,description=Flow contains js code which defines how the template should be executed,type=string"`
|
||||
// description: |
|
||||
// Requests contains the http request to make in the template.
|
||||
// WARNING: 'requests' will be deprecated and will be removed in a future release. Please use 'http' instead.
|
||||
@@ -161,6 +163,11 @@ type Template struct {
|
||||
ImportedFiles []string `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (template Template) JSONSchemaExtend(base *jsonschema.Schema) {
|
||||
schema.ExtendSchema(templateMetadata, base)
|
||||
schema.ApplyAnyOfRequired(templateAnyOfRequired, base)
|
||||
}
|
||||
|
||||
// Type returns the type of the template
|
||||
func (template *Template) Type() types.ProtocolType {
|
||||
switch {
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
package templates
|
||||
|
||||
import "github.com/projectdiscovery/nuclei/v3/pkg/utils/schema"
|
||||
|
||||
// contains metadata information about template
|
||||
var templateMetadata = []schema.PropertyMetadata{
|
||||
{
|
||||
PropName: "id",
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"ID is the unique id for the template.",
|
||||
" ### Good IDs ",
|
||||
"A good ID uniquely identifies what the requests in the template",
|
||||
"are doing. Let's say you have a template that identifies a git-config",
|
||||
"file on the webservers, a good name would be `git-config-exposure`. Another",
|
||||
"example name is `azure-apps-nxdomain-takeover`",
|
||||
),
|
||||
Example: schema.PropertyExamples("example-id", "git-config-exposure", "azure-apps-nxdomain-takeover", "cve-2021-19520"),
|
||||
},
|
||||
{
|
||||
PropName: "flow",
|
||||
PropType: "string",
|
||||
Description: schema.MultiLine(
|
||||
"Flow describes how multiple request-blocks/protocols should be combined together and executed",
|
||||
"It is a javascript code where each protocol is a function and all request-blocks are indexed by their corresponding index in request array",
|
||||
"Example: ",
|
||||
" flow: http(1) && http(2)",
|
||||
" means that second http request will be executed only if first http request is successful (aka matched)",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
"http(1) && http(2)",
|
||||
schema.MultiLine(
|
||||
" | # example-vhost-enum",
|
||||
"\tssl(); // -> execute all ssl protocol requests",
|
||||
"\tdns(); // -> execute all dns protocol requests",
|
||||
"\tfor (let got of template.domains) { // -> iterate over 'domains' array variable",
|
||||
"\t\tset('vhost', got); // -> set 'vhost' variable to current domain",
|
||||
"\t\thttp(); // -> execute all http protocol requests",
|
||||
"\t}\n",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "requests",
|
||||
Deprecated: true,
|
||||
},
|
||||
{
|
||||
PropName: "network",
|
||||
Deprecated: true,
|
||||
},
|
||||
{
|
||||
PropName: "signature",
|
||||
RemoveRef: true,
|
||||
PropType: "string",
|
||||
Deprecated: true,
|
||||
Example: schema.PropertyExample("aws"),
|
||||
},
|
||||
{
|
||||
PropName: "self-contained",
|
||||
Description: schema.MultiLine(
|
||||
"Self-contained marks all requests in this template as independent of input which means input/target is not required for execution of template",
|
||||
"but other variables defined in template need to be explicitly set using -V flag",
|
||||
"Default value is false",
|
||||
"Note: self-contained templates only run once regardless of how many targets where provided to nuclei",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"self-contained: true",
|
||||
"```",
|
||||
"Full example template of self-contained is available at https://cloud.projectdiscovery.io/public/aws-app-enum",
|
||||
),
|
||||
Default: false,
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "info",
|
||||
Description: schema.MultiLine(
|
||||
"Info contains the required metadata information about the template",
|
||||
"It is meant to provide basic but necessary info like name, author , severity",
|
||||
"along with many other optional fields like metadata, classification etc",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"info: ",
|
||||
"\tname: Example Template",
|
||||
"\tauthor: pdteam",
|
||||
"\tseverity: info",
|
||||
"\tdescription: This is an example template",
|
||||
"```",
|
||||
"\nNote: - ",
|
||||
"For a template to be valid name,author,severity of `info` section must be set",
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "stop-at-first-match",
|
||||
Description: schema.MultiLine(
|
||||
"stop-at-first-match stops the execution of template as soon as first match/result was found in a template given that template was sending multiple requests",
|
||||
"this is required in case of default-login , brute-force and even detection templates where multiple requests are sent from template but we want to exit as soon as first match/result was found",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"stop-at-first-match: true",
|
||||
"```",
|
||||
"Full example template of stop-at-first-match is available at https://cloud.projectdiscovery.io/public/bitbucket-public-repository",
|
||||
),
|
||||
Default: false,
|
||||
Example: schema.PropertyExample(true),
|
||||
},
|
||||
{
|
||||
PropName: "variables",
|
||||
PropType: "object",
|
||||
RemoveRef: true,
|
||||
Description: schema.MultiLine(
|
||||
"Variables are the global variables that once defined here can be used anywhere in the template",
|
||||
"Variables are evaluated in the order they are defined so one variable can be referenced in another variable",
|
||||
"Variables are evaluated before sending every requests so one can reference any variables that are available at runtime and they will be evaluate and used when referenced",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"variables:",
|
||||
` oast: {{interact-sh}}`,
|
||||
` payload: "{{base64(oast)}}"`,
|
||||
"```",
|
||||
"Full example template of variables is available at https://cloud.projectdiscovery.io/public/screenshot",
|
||||
"Note: -",
|
||||
"These variables can be overridden by -V flag at runtime if needed",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
"\n\tdomain: \"{{FQDN}}\"",
|
||||
"\temail: pdteam@{{domain}}",
|
||||
),
|
||||
schema.MultiLine(
|
||||
"\n\toast: \"{{interact-sh}}\"",
|
||||
"\tpayload: \"{{base64(oast)}}\"",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "constants",
|
||||
PropType: "object",
|
||||
RemoveRef: true,
|
||||
Description: schema.MultiLine(
|
||||
"Constants are the global constants that once defined here can be used anywhere in the template",
|
||||
"It can be used in same way as variables but only difference is that constants cannot be overridden by -V flag at runtime",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"constants:",
|
||||
` exploit: 'x0x0x0x0x0x0x'`,
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExample(
|
||||
schema.MultiLine(
|
||||
"\n\texploit: 'x0x0x0x0x0x0x'",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "requests",
|
||||
Deprecated: true,
|
||||
Description: schema.MultiLine(
|
||||
"HTTP is the protocol block for sending HTTP requests",
|
||||
"It is of array type and can contain multiple requests",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"requests: ",
|
||||
"\t- method: GET",
|
||||
"\t\tpath:",
|
||||
"\t\t\t- \"{{BaseURL}}\"",
|
||||
"\t\t",
|
||||
"\t\tmatchers:",
|
||||
"\t\t\t- type: status",
|
||||
"\t\t\tstatus:",
|
||||
"\t\t\t\t- 200",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
" # http get",
|
||||
"\t- method: GET",
|
||||
"\t\tpath:",
|
||||
"\t\t\t- \"{{BaseURL}}\"",
|
||||
"\t\t",
|
||||
"\t\tmatchers:",
|
||||
"\t\t\t- type: status",
|
||||
"\t\t\t\tstatus:",
|
||||
"\t\t\t\t\t- 200",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # raw get",
|
||||
"\t- raw:",
|
||||
"\t\t\t- |",
|
||||
"\t\t\t\t\tGET HTTP/1.1",
|
||||
"\t\t\t\t\tHost: {{Hostname}}",
|
||||
"\t\t\t\t",
|
||||
"\t\t\tmatchers:",
|
||||
"\t\t\t- type: status",
|
||||
"\t\t\t\tstatus:",
|
||||
"\t\t\t\t\t- 200",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # http self contained",
|
||||
"\t- method: GET",
|
||||
"\t\tpath:",
|
||||
"\t\t - \"https://api.stripe.com/v1/charges\"",
|
||||
"\t\t",
|
||||
"\t\tself-contained: true",
|
||||
"\t\theaders:",
|
||||
"\t\t\t"+`Authorization: 'Basic {{base64(token)}}'`,
|
||||
"\t\t",
|
||||
"\t\tmatchers:",
|
||||
"\t\t - type: word",
|
||||
"\t\t part: body",
|
||||
"\t\t words:",
|
||||
"\t\t - '\"object\":'",
|
||||
"\t\t - '\"url\":'",
|
||||
"\t\t - '\"data\":'",
|
||||
"\t\t condition: and",
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
PropName: "http",
|
||||
Description: schema.MultiLine(
|
||||
"HTTP is the protocol block for sending HTTP requests",
|
||||
"It is of array type and can contain multiple requests",
|
||||
"Example: ",
|
||||
"```yaml",
|
||||
"http: ",
|
||||
"\t- method: GET",
|
||||
"\t\tpath:",
|
||||
"\t\t\t- \"{{BaseURL}}\"",
|
||||
"\t\t",
|
||||
"\t\tmatchers:",
|
||||
"\t\t\t- type: status",
|
||||
"\t\t\tstatus:",
|
||||
"\t\t\t\t- 200",
|
||||
"```",
|
||||
),
|
||||
Example: schema.PropertyExamples(
|
||||
schema.MultiLine(
|
||||
" # http get",
|
||||
"\t- method: GET",
|
||||
"\t\tpath:",
|
||||
"\t\t\t- \"{{BaseURL}}\"",
|
||||
"\t\t",
|
||||
"\t\tmatchers:",
|
||||
"\t\t\t- type: status",
|
||||
"\t\t\t\tstatus:",
|
||||
"\t\t\t\t\t- 200",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # raw get",
|
||||
"\t- raw:",
|
||||
"\t\t\t- |",
|
||||
"\t\t\t\t\tGET HTTP/1.1",
|
||||
"\t\t\t\t\tHost: {{Hostname}}",
|
||||
"\t\t\t\t",
|
||||
"\t\t\tmatchers:",
|
||||
"\t\t\t- type: status",
|
||||
"\t\t\t\tstatus:",
|
||||
"\t\t\t\t\t- 200",
|
||||
),
|
||||
schema.MultiLine(
|
||||
" # http self contained",
|
||||
"\t- method: GET",
|
||||
"\t\tpath:",
|
||||
"\t\t - \"https://api.stripe.com/v1/charges\"",
|
||||
"\t\t",
|
||||
"\t\tself-contained: true",
|
||||
"\t\theaders:",
|
||||
"\t\t\t"+`Authorization: 'Basic {{base64(token)}}'`,
|
||||
"\t\t",
|
||||
"\t\tmatchers:",
|
||||
"\t\t - type: word",
|
||||
"\t\t part: body",
|
||||
"\t\t words:",
|
||||
"\t\t - '\"object\":'",
|
||||
"\t\t - '\"url\":'",
|
||||
"\t\t - '\"data\":'",
|
||||
"\t\t condition: and",
|
||||
),
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
// valid template should contain at least on below combinations
|
||||
// requireBase uses two arguments to generate all combinations in format of
|
||||
// base_[0]
|
||||
// base_[1] etc
|
||||
var templateAnyOfRequired = []schema.RequiredCombos{
|
||||
schema.RequireBase([]string{"id", "info"},
|
||||
schema.Require("http"),
|
||||
schema.Require("dns"),
|
||||
schema.Require("file"),
|
||||
schema.Require("tcp"),
|
||||
schema.Require("headless"),
|
||||
schema.Require("ssl"),
|
||||
schema.Require("websocket"),
|
||||
schema.Require("whois"),
|
||||
schema.Require("code"),
|
||||
schema.Require("javascript"),
|
||||
schema.Require("requests"),
|
||||
schema.Require("network"),
|
||||
),
|
||||
schema.Require("workflows"),
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// schema implements helper types & functions for generating better json schema
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/invopop/jsonschema"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
)
|
||||
|
||||
// PropertyMetadata is a metadata for a property in a schema / struct
|
||||
type PropertyMetadata struct {
|
||||
PropName string
|
||||
Description string
|
||||
PropType string
|
||||
Example []interface{}
|
||||
Default any
|
||||
OneOf []*PropertyMetadata
|
||||
RemoveRef bool
|
||||
Deprecated bool
|
||||
}
|
||||
|
||||
// PropertyExamples returns a list of examples for a property
|
||||
func PropertyExamples(values ...any) []interface{} {
|
||||
examples := []interface{}{}
|
||||
for _, value := range values {
|
||||
if value != nil {
|
||||
examples = append(examples, value)
|
||||
}
|
||||
}
|
||||
return examples
|
||||
}
|
||||
|
||||
// PropertyExample returns a list of examples for a property
|
||||
func PropertyExample(values any) []interface{} {
|
||||
return []interface{}{values}
|
||||
}
|
||||
|
||||
// TrimmedString trims the string and returns it
|
||||
func TrimmedString(value string) string {
|
||||
return strings.TrimSpace(value)
|
||||
}
|
||||
|
||||
func MultiLine(values ...string) string {
|
||||
return strings.Join(values, "\n")
|
||||
}
|
||||
|
||||
// ExtendSchema extends the schema with the metadata
|
||||
// This could be patching or adding additional information to the schema
|
||||
func ExtendSchema(metadata []PropertyMetadata, base *jsonschema.Schema) {
|
||||
for _, meta := range metadata {
|
||||
if prop, ok := base.Properties.Get(meta.PropName); ok {
|
||||
// if it has oneof, we need to add it
|
||||
if len(meta.OneOf) > 0 {
|
||||
for _, oneOf := range meta.OneOf {
|
||||
prop.OneOf = append(prop.OneOf, &jsonschema.Schema{
|
||||
Type: oneOf.PropType,
|
||||
Description: oneOf.Description,
|
||||
Examples: oneOf.Example,
|
||||
Default: oneOf.Default,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if meta.PropType != "" {
|
||||
prop.Type = meta.PropType
|
||||
}
|
||||
if meta.Description != "" {
|
||||
prop.Description = meta.Description
|
||||
}
|
||||
if len(meta.Example) > 0 {
|
||||
prop.Examples = append(prop.Examples, meta.Example...)
|
||||
prop.Examples = sliceutil.Dedupe(prop.Examples)
|
||||
}
|
||||
if meta.Default != nil {
|
||||
prop.Default = meta.Default
|
||||
}
|
||||
|
||||
}
|
||||
prop.Examples = purgeNil(prop.Examples)
|
||||
if meta.RemoveRef {
|
||||
prop.Ref = ""
|
||||
}
|
||||
prop.Deprecated = meta.Deprecated
|
||||
// add new property called markdownDescription
|
||||
if prop.Description != "" {
|
||||
base.Properties.Set("markdownDescription", &jsonschema.Schema{
|
||||
Type: "string",
|
||||
Description: prop.Description,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequiredCombos is a list of required field combinations
|
||||
// and at least on of it is inforced if none is satisfied
|
||||
type RequiredCombos struct {
|
||||
RequireBase []string
|
||||
Require []string
|
||||
required []RequiredCombos
|
||||
}
|
||||
|
||||
func RequireBase(base []string, requires ...RequiredCombos) RequiredCombos {
|
||||
x := RequiredCombos{RequireBase: base}
|
||||
x.required = requires
|
||||
return x
|
||||
}
|
||||
|
||||
func Require(require ...string) RequiredCombos {
|
||||
return RequiredCombos{Require: require}
|
||||
}
|
||||
|
||||
// ApplyAnyOfRequired applies any of required field combinations
|
||||
func ApplyAnyOfRequired(meta []RequiredCombos, base *jsonschema.Schema) {
|
||||
if len(meta) == 0 {
|
||||
return
|
||||
}
|
||||
for _, anyOf := range meta {
|
||||
if len(anyOf.Require) == 0 && len(anyOf.RequireBase) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(anyOf.RequireBase) > 0 && len(anyOf.required) > 0 {
|
||||
// iterate over all required combinations present
|
||||
// in required base and add them to the anyof
|
||||
for _, r := range anyOf.required {
|
||||
required := sliceutil.Clone(anyOf.RequireBase)
|
||||
required = append(required, r.Require...)
|
||||
base.AnyOf = append(base.AnyOf, &jsonschema.Schema{
|
||||
Required: required,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(anyOf.Require) > 0 {
|
||||
base.AnyOf = append(base.AnyOf, &jsonschema.Schema{
|
||||
Required: anyOf.Require,
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func purgeNil(s []any) []any {
|
||||
var r []any
|
||||
for _, i := range s {
|
||||
if i != nil {
|
||||
r = append(r, i)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
Reference in new issue
Block a user