Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/docgen/docgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"log"
"os"
"reflect"
"regexp"
"strings"

Expand Down Expand Up @@ -33,6 +34,12 @@ func main() {

// Generate jsonschema
r := &jsonschema.Reflector{}
r.Namer = func(r reflect.Type) string {
if r.Kind() == reflect.Slice {
return ""
}
return r.String()
}
jsonschemaData := r.Reflect(&templates.Template{})

var buf bytes.Buffer
Expand Down
39 changes: 39 additions & 0 deletions pkg/fuzz/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"

"github.com/invopop/jsonschema"
mapsutil "github.com/projectdiscovery/utils/maps"
"gopkg.in/yaml.v2"
)
Expand All @@ -29,6 +30,44 @@ type SliceOrMapSlice struct {
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.
func (v *SliceOrMapSlice) UnmarshalJSON(data []byte) error {
// try to unmashal as a string and fallback to map
Expand Down
2 changes: 1 addition & 1 deletion pkg/model/types/userAgent/user_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type UserAgentHolder struct {
Value UserAgent `mapping:"true"`
}

func (userAgentHolder UserAgentHolder) JSONSchemaType() *jsonschema.Schema {
func (userAgentHolder UserAgentHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "userAgent for the headless",
Expand Down
2 changes: 1 addition & 1 deletion pkg/operators/extractors/extractor_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ type ExtractorTypeHolder struct {
ExtractorType ExtractorType `mapping:"true"`
}

func (holder ExtractorTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder ExtractorTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "type of the extractor",
Expand Down
2 changes: 1 addition & 1 deletion pkg/operators/matchers/matchers_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (t MatcherTypeHolder) String() string {
return t.MatcherType.String()
}

func (holder MatcherTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder MatcherTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "type of the matcher",
Expand Down
10 changes: 5 additions & 5 deletions pkg/protocols/code/code.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,25 @@ var (
type Request struct {
// Operators for the current request go here.
operators.Operators `yaml:",inline,omitempty"`
CompiledOperators *operators.Operators `yaml:"-"`
CompiledOperators *operators.Operators `yaml:"-" json:"-"`

// 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: |
// 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: |
// 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: |
// 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: |
// 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: |
// 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:"-"`
preConditionCompiled *goja.Program `yaml:"-" json:"-"`
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocols/common/generators/attack_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type AttackTypeHolder struct {
Value AttackType `mapping:"true"`
}

func (holder AttackTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder AttackTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "type of the attack",
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocols/common/variables/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Variable struct {
utils.InsertionOrderedStringMap `yaml:"-" json:"-"`
}

func (variables Variable) JSONSchemaType() *jsonschema.Schema {
func (variables Variable) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "object",
Title: "variables for the request",
Expand Down
4 changes: 2 additions & 2 deletions pkg/protocols/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ type Request struct {
// examples:
// - name: Use a retry of 100 to 150 generally
// 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: |
// Attack is the type of payload combinations to perform.
Expand All @@ -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"`
generator *generators.PayloadGenerator

CompiledOperators *operators.Operators `yaml:"-"`
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
dnsClient *retryabledns.Client
options *protocols.ExecutorOptions

Expand Down
2 changes: 1 addition & 1 deletion pkg/protocols/dns/dns_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (holder DNSRequestTypeHolder) String() string {
return holder.DNSRequestType.String()
}

func (holder DNSRequestTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder DNSRequestTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "type of DNS request to make",
Expand Down
29 changes: 28 additions & 1 deletion pkg/protocols/headless/engine/action.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package engine

import "strings"
import (
"strings"

"github.com/invopop/jsonschema"
)

// Action is an action taken by the browser to reach a navigation
//
Expand Down Expand Up @@ -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"`
}

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
func (a *Action) String() string {
builder := &strings.Builder{}
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocols/headless/engine/action_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ type ActionTypeHolder struct {
func (holder ActionTypeHolder) String() string {
return holder.ActionType.String()
}
func (holder ActionTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder ActionTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "action to perform",
Expand Down
24 changes: 24 additions & 0 deletions pkg/protocols/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"strings"

"github.com/invopop/jsonschema"
json "github.com/json-iterator/go"
"github.com/pkg/errors"

Expand Down Expand Up @@ -219,6 +220,29 @@ type Request struct {
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
func (r *Request) Options() *protocols.ExecutorOptions {
return r.options
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocols/http/http_method_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (holder HTTPMethodTypeHolder) String() string {
return holder.MethodType.String()
}

func (holder HTTPMethodTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder HTTPMethodTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "method is the HTTP request method",
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocols/http/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type SignatureTypeHolder struct {
Value SignatureType
}

func (holder SignatureTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder SignatureTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "type of the signature",
Expand Down
6 changes: 3 additions & 3 deletions pkg/protocols/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type Request struct {
// description: |
// 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
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: |
// 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
Expand Down Expand Up @@ -91,7 +91,7 @@ type Request struct {

// Operators for the current request go here.
operators.Operators `yaml:",inline,omitempty"`
CompiledOperators *operators.Operators `yaml:"-"`
CompiledOperators *operators.Operators `yaml:"-" json:"-"`

generator *generators.PayloadGenerator
// cache any variables that may be needed for operation.
Expand Down Expand Up @@ -128,7 +128,7 @@ type Input struct {
// examples:
// - value: "\"TEST\""
// - 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: |
// Type is the type of input specified in `data` field.
//
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocols/network/network_input_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (holder NetworkInputTypeHolder) String() string {
return holder.NetworkInputType.String()
}

func (holder NetworkInputTypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder NetworkInputTypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "type is the type of input data",
Expand Down
2 changes: 1 addition & 1 deletion pkg/templates/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type TypeHolder struct {
ProtocolType ProtocolType `mapping:"true"`
}

func (holder TypeHolder) JSONSchemaType() *jsonschema.Schema {
func (holder TypeHolder) JSONSchema() *jsonschema.Schema {
gotType := &jsonschema.Schema{
Type: "string",
Title: "type of the protocol",
Expand Down