fix(schema): generation of missing JSON schema definitions (#4995)

* fix(schema): generation of missing JSON schema definitions

* make headers and data to accept multi-type inputs

* misc update
dev
Ramana Reddy 2024-04-08 03:29:42 +05:30 committed by GitHub
parent 82e25f6631
commit 8c27ca2591
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 119 additions and 22 deletions

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"log" "log"
"os" "os"
"reflect"
"regexp" "regexp"
"strings" "strings"
@ -33,6 +34,12 @@ func main() {
// Generate jsonschema // Generate jsonschema
r := &jsonschema.Reflector{} r := &jsonschema.Reflector{}
r.Namer = func(r reflect.Type) string {
if r.Kind() == reflect.Slice {
return ""
}
return r.String()
}
jsonschemaData := r.Reflect(&templates.Template{}) jsonschemaData := r.Reflect(&templates.Template{})
var buf bytes.Buffer var buf bytes.Buffer

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/invopop/jsonschema"
mapsutil "github.com/projectdiscovery/utils/maps" mapsutil "github.com/projectdiscovery/utils/maps"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -29,6 +30,44 @@ type SliceOrMapSlice struct {
KV *mapsutil.OrderedMap[string, string] KV *mapsutil.OrderedMap[string, string]
} }
func (v SliceOrMapSlice) JSONSchemaExtend(schema *jsonschema.Schema) *jsonschema.Schema {
schema = &jsonschema.Schema{
Title: schema.Title,
Description: schema.Description,
Type: "array",
Items: &jsonschema.Schema{
OneOf: []*jsonschema.Schema{
{
Type: "string",
},
{
Type: "object",
},
},
},
}
return schema
}
func (v SliceOrMapSlice) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Title: "Payloads of Fuzz Rule",
Description: "Payloads to perform fuzzing substitutions with.",
Type: "array",
Items: &jsonschema.Schema{
OneOf: []*jsonschema.Schema{
{
Type: "string",
},
{
Type: "object",
},
},
},
}
return gotType
}
// UnmarshalJSON implements json.Unmarshaler interface. // UnmarshalJSON implements json.Unmarshaler interface.
func (v *SliceOrMapSlice) UnmarshalJSON(data []byte) error { func (v *SliceOrMapSlice) UnmarshalJSON(data []byte) error {
// try to unmashal as a string and fallback to map // try to unmashal as a string and fallback to map

View File

@ -61,7 +61,7 @@ type UserAgentHolder struct {
Value UserAgent `mapping:"true"` Value UserAgent `mapping:"true"`
} }
func (userAgentHolder UserAgentHolder) JSONSchemaType() *jsonschema.Schema { func (userAgentHolder UserAgentHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "userAgent for the headless", Title: "userAgent for the headless",

View File

@ -72,7 +72,7 @@ type ExtractorTypeHolder struct {
ExtractorType ExtractorType `mapping:"true"` ExtractorType ExtractorType `mapping:"true"`
} }
func (holder ExtractorTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder ExtractorTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "type of the extractor", Title: "type of the extractor",

View File

@ -82,7 +82,7 @@ func (t MatcherTypeHolder) String() string {
return t.MatcherType.String() return t.MatcherType.String()
} }
func (holder MatcherTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder MatcherTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "type of the matcher", Title: "type of the matcher",

View File

@ -48,25 +48,25 @@ var (
type Request struct { type Request struct {
// Operators for the current request go here. // Operators for the current request go here.
operators.Operators `yaml:",inline,omitempty"` operators.Operators `yaml:",inline,omitempty"`
CompiledOperators *operators.Operators `yaml:"-"` CompiledOperators *operators.Operators `yaml:"-" json:"-"`
// ID is the optional id of the request // ID is the optional id of the request
ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID is the optional ID of the Request"` ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID is the optional ID of the Request"`
// description: | // description: |
// Engine type // Engine type
Engine []string `yaml:"engine,omitempty" jsonschema:"title=engine,description=Engine"` Engine []string `yaml:"engine,omitempty" json:"engine,omitempty" jsonschema:"title=engine,description=Engine"`
// description: | // description: |
// PreCondition is a condition which is evaluated before sending the request. // PreCondition is a condition which is evaluated before sending the request.
PreCondition string `yaml:"pre-condition,omitempty" json:"pre-condition,omitempty" jsonschema:"title=pre-condition for the request,description=PreCondition is a condition which is evaluated before sending the request"` PreCondition string `yaml:"pre-condition,omitempty" json:"pre-condition,omitempty" jsonschema:"title=pre-condition for the request,description=PreCondition is a condition which is evaluated before sending the request"`
// description: | // description: |
// Engine Arguments // Engine Arguments
Args []string `yaml:"args,omitempty" jsonschema:"title=args,description=Args"` Args []string `yaml:"args,omitempty" json:"args,omitempty" jsonschema:"title=args,description=Args"`
// description: | // description: |
// Pattern preferred for file name // Pattern preferred for file name
Pattern string `yaml:"pattern,omitempty" jsonschema:"title=pattern,description=Pattern"` Pattern string `yaml:"pattern,omitempty" json:"pattern,omitempty" jsonschema:"title=pattern,description=Pattern"`
// description: | // description: |
// Source File/Snippet // Source File/Snippet
Source string `yaml:"source,omitempty" jsonschema:"title=source file/snippet,description=Source snippet"` Source string `yaml:"source,omitempty" json:"source,omitempty" jsonschema:"title=source file/snippet,description=Source snippet"`
options *protocols.ExecutorOptions `yaml:"-" json:"-"` options *protocols.ExecutorOptions `yaml:"-" json:"-"`
preConditionCompiled *goja.Program `yaml:"-" json:"-"` preConditionCompiled *goja.Program `yaml:"-" json:"-"`

View File

@ -61,7 +61,7 @@ type AttackTypeHolder struct {
Value AttackType `mapping:"true"` Value AttackType `mapping:"true"`
} }
func (holder AttackTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder AttackTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "type of the attack", Title: "type of the attack",

View File

@ -21,7 +21,7 @@ type Variable struct {
utils.InsertionOrderedStringMap `yaml:"-" json:"-"` utils.InsertionOrderedStringMap `yaml:"-" json:"-"`
} }
func (variables Variable) JSONSchemaType() *jsonschema.Schema { func (variables Variable) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "object", Type: "object",
Title: "variables for the request", Title: "variables for the request",

View File

@ -60,7 +60,7 @@ type Request struct {
// examples: // examples:
// - name: Use a retry of 100 to 150 generally // - name: Use a retry of 100 to 150 generally
// value: 100 // value: 100
TraceMaxRecursion int `yaml:"trace-max-recursion,omitempty" jsonschema:"title=trace-max-recursion level for dns request,description=TraceMaxRecursion is the number of max recursion allowed for trace operations"` TraceMaxRecursion int `yaml:"trace-max-recursion,omitempty" json:"trace-max-recursion,omitempty" jsonschema:"title=trace-max-recursion level for dns request,description=TraceMaxRecursion is the number of max recursion allowed for trace operations"`
// description: | // description: |
// Attack is the type of payload combinations to perform. // Attack is the type of payload combinations to perform.
@ -83,7 +83,7 @@ type Request struct {
Threads int `yaml:"threads,omitempty" json:"threads,omitempty" jsonschema:"title=threads for sending requests,description=Threads specifies number of threads to use sending requests. This enables Connection Pooling"` Threads int `yaml:"threads,omitempty" json:"threads,omitempty" jsonschema:"title=threads for sending requests,description=Threads specifies number of threads to use sending requests. This enables Connection Pooling"`
generator *generators.PayloadGenerator generator *generators.PayloadGenerator
CompiledOperators *operators.Operators `yaml:"-"` CompiledOperators *operators.Operators `yaml:"-" json:"-"`
dnsClient *retryabledns.Client dnsClient *retryabledns.Client
options *protocols.ExecutorOptions options *protocols.ExecutorOptions

View File

@ -92,7 +92,7 @@ func (holder DNSRequestTypeHolder) String() string {
return holder.DNSRequestType.String() return holder.DNSRequestType.String()
} }
func (holder DNSRequestTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder DNSRequestTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "type of DNS request to make", Title: "type of DNS request to make",

View File

@ -1,6 +1,10 @@
package engine package engine
import "strings" import (
"strings"
"github.com/invopop/jsonschema"
)
// Action is an action taken by the browser to reach a navigation // Action is an action taken by the browser to reach a navigation
// //
@ -29,6 +33,29 @@ type Action struct {
ActionType ActionTypeHolder `yaml:"action" json:"action" jsonschema:"title=action to perform,description=Type of actions to perform,enum=navigate,enum=script,enum=click,enum=rightclick,enum=text,enum=screenshot,enum=time,enum=select,enum=files,enum=waitload,enum=getresource,enum=extract,enum=setmethod,enum=addheader,enum=setheader,enum=deleteheader,enum=setbody,enum=waitevent,enum=keyboard,enum=debug,enum=sleep"` ActionType ActionTypeHolder `yaml:"action" json:"action" jsonschema:"title=action to perform,description=Type of actions to perform,enum=navigate,enum=script,enum=click,enum=rightclick,enum=text,enum=screenshot,enum=time,enum=select,enum=files,enum=waitload,enum=getresource,enum=extract,enum=setmethod,enum=addheader,enum=setheader,enum=deleteheader,enum=setbody,enum=waitevent,enum=keyboard,enum=debug,enum=sleep"`
} }
func (a Action) JSONSchemaExtend(schema *jsonschema.Schema) {
argsSchema, ok := schema.Properties.Get("args")
if !ok {
return
}
argsSchema.PatternProperties = map[string]*jsonschema.Schema{
".*": {
OneOf: []*jsonschema.Schema{
{
Type: "string",
},
{
Type: "integer",
},
{
Type: "boolean",
},
},
},
}
argsSchema.Ref = ""
}
// String returns the string representation of an action // String returns the string representation of an action
func (a *Action) String() string { func (a *Action) String() string {
builder := &strings.Builder{} builder := &strings.Builder{}

View File

@ -171,7 +171,7 @@ type ActionTypeHolder struct {
func (holder ActionTypeHolder) String() string { func (holder ActionTypeHolder) String() string {
return holder.ActionType.String() return holder.ActionType.String()
} }
func (holder ActionTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder ActionTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "action to perform", Title: "action to perform",

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/invopop/jsonschema"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -219,6 +220,29 @@ type Request struct {
fuzzPreConditionOperator matchers.ConditionType `yaml:"-" json:"-"` fuzzPreConditionOperator matchers.ConditionType `yaml:"-" json:"-"`
} }
func (e Request) JSONSchemaExtend(schema *jsonschema.Schema) {
headersSchema, ok := schema.Properties.Get("headers")
if !ok {
return
}
headersSchema.PatternProperties = map[string]*jsonschema.Schema{
".*": {
OneOf: []*jsonschema.Schema{
{
Type: "string",
},
{
Type: "integer",
},
{
Type: "boolean",
},
},
},
}
headersSchema.Ref = ""
}
// Options returns executer options for http request // Options returns executer options for http request
func (r *Request) Options() *protocols.ExecutorOptions { func (r *Request) Options() *protocols.ExecutorOptions {
return r.options return r.options

View File

@ -89,7 +89,7 @@ func (holder HTTPMethodTypeHolder) String() string {
return holder.MethodType.String() return holder.MethodType.String()
} }
func (holder HTTPMethodTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder HTTPMethodTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "method is the HTTP request method", Title: "method is the HTTP request method",

View File

@ -51,7 +51,7 @@ type SignatureTypeHolder struct {
Value SignatureType Value SignatureType
} }
func (holder SignatureTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder SignatureTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "type of the signature", Title: "type of the signature",

View File

@ -61,7 +61,7 @@ type Request struct {
// description: | // description: |
// Port is the port to send network requests to. this acts as default port but is overriden if target/input contains // Port is the port to send network requests to. this acts as default port but is overriden if target/input contains
// non-http(s) ports like 80,8080,8081 etc // non-http(s) ports like 80,8080,8081 etc
Port string `yaml:"port,omitempty" json:"port,omitempty" jsonschema:"title=port to send requests to,description=Port to send network requests to"` Port string `yaml:"port,omitempty" json:"port,omitempty" jsonschema:"title=port to send requests to,description=Port to send network requests to,oneof_type=string;integer"`
// description: | // description: |
// ExcludePorts is the list of ports to exclude from being scanned . It is intended to be used with `Port` field and contains a list of ports which are ignored/skipped // ExcludePorts is the list of ports to exclude from being scanned . It is intended to be used with `Port` field and contains a list of ports which are ignored/skipped
@ -91,7 +91,7 @@ type Request struct {
// Operators for the current request go here. // Operators for the current request go here.
operators.Operators `yaml:",inline,omitempty"` operators.Operators `yaml:",inline,omitempty"`
CompiledOperators *operators.Operators `yaml:"-"` CompiledOperators *operators.Operators `yaml:"-" json:"-"`
generator *generators.PayloadGenerator generator *generators.PayloadGenerator
// cache any variables that may be needed for operation. // cache any variables that may be needed for operation.
@ -128,7 +128,7 @@ type Input struct {
// examples: // examples:
// - value: "\"TEST\"" // - value: "\"TEST\""
// - value: "\"hex_decode('50494e47')\"" // - value: "\"hex_decode('50494e47')\""
Data string `yaml:"data,omitempty" json:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input"` Data string `yaml:"data,omitempty" json:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input,oneof_type=string;integer"`
// description: | // description: |
// Type is the type of input specified in `data` field. // Type is the type of input specified in `data` field.
// //

View File

@ -66,7 +66,7 @@ func (holder NetworkInputTypeHolder) String() string {
return holder.NetworkInputType.String() return holder.NetworkInputType.String()
} }
func (holder NetworkInputTypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder NetworkInputTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "type is the type of input data", Title: "type is the type of input data",

View File

@ -92,7 +92,7 @@ type TypeHolder struct {
ProtocolType ProtocolType `mapping:"true"` ProtocolType ProtocolType `mapping:"true"`
} }
func (holder TypeHolder) JSONSchemaType() *jsonschema.Schema { func (holder TypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{ gotType := &jsonschema.Schema{
Type: "string", Type: "string",
Title: "type of the protocol", Title: "type of the protocol",