Added new YAML based doc to structures

dev
Ice3man543 2021-07-27 16:03:56 +05:30
parent 543cfdd309
commit d04511494d
12 changed files with 478 additions and 98 deletions

View File

@ -4,28 +4,70 @@ import "regexp"
// Extractor is used to extract part of response using a regex.
type Extractor struct {
// Name is the extractor's name
// description: |
// Name of the extractor. Name should be lowercase and must not contain
// spaces or dashes (-).
// examples:
// - value: "\"cookie-extractor\""
Name string `yaml:"name,omitempty"`
// Type is the type of the extractor
// description: |
// Type is the type of the extractor.
// values:
// - "regex"
// - "kval"
Type string `yaml:"type"`
// extractorType is the internal type of the extractor
extractorType ExtractorType
// Regex are the regex pattern required to be present in the response
// description: |
// Regex contains the regular expression patterns to exract from a part.
//
// Go regex engine does not supports lookaheads or lookbehinds, so as a result
// they are also not supported in nuclei.
// examples:
// - name: Braintree Access Token Regex
// value: >
// []string{"access_token\\$production\\$[0-9a-z]{16}\\$[0-9a-f]{32}"}
// - name: Wordpress Author Extraction regex
// value: >
// []string{"Author:(?:[A-Za-z0-9 -\\_=\"]+)?<span(?:[A-Za-z0-9 -\\_=\"]+)?>([A-Za-z0-9]+)<\\/span>"}
Regex []string `yaml:"regex"`
// RegexGroup specifies a group to extract from the regex
// description: |
// Group specifies a numbered group to extract from the regex.
// examples:
// - name: Example Regex Group
// - value: "1"
RegexGroup int `yaml:"group"`
// regexCompiled is the compiled variant
regexCompiled []*regexp.Regexp
// KVal are the kval to be present in the response headers/cookies
// description: |
// kval contains the key-value pairs required in the response.
//
// Each protocol exposes a lot of different data in response. The kval
// extractor can be used to extract those key-value pairs. A list of
// supported parts is available in docs for request types.
// examples:
// - name: Extract Server Header From HTTP Response
// value: >
// []string{"Server"}
// - name: Extracting value of PHPSESSID Cookie
// value: >
// []string{"PHPSESSID"}
KVal []string `yaml:"kval,omitempty"`
// Part is the part of the request to match
// description: |
// Part is the part of the request response to extract data from.
//
// By default, matching is performed in request body.
// Each protocol exposes a lot of different parts which are well
// documented in docs for each request type.
// examples:
// - value: "\"body\""
// - value: "\"raw\""
Part string `yaml:"part,omitempty"`
// Internal defines if this is used internally
// description: |
// Internal, when set to true will allow using the value extracted
// in the next request for some protocols (like HTTP).
Internal bool `yaml:"internal,omitempty"`
}

View File

@ -8,35 +8,102 @@ import (
// Matcher is used to match a part in the output from a protocol.
type Matcher struct {
// Type is the type of the matcher
// description: |
// Type is the type of the matcher.
// values:
// - "status"
// - "size"
// - "word"
// - "regex"
// - "binary"
// - "dsl"
Type string `yaml:"type"`
// Condition is the optional condition between two matcher variables
//
// By default, the condition is assumed to be OR.
// description: |
// Condition is the optional condition between two matcher variables. By default,
// the condition is assumed to be OR.
// values:
// - "and"
// - "or"
Condition string `yaml:"condition,omitempty"`
// Part is the part of the data to match
// description: |
// Part is the part of the request response to match data from.
//
// Each protocol exposes a lot of different parts which are well
// documented in docs for each request type.
// examples:
// - value: "\"body\""
// - value: "\"raw\""
Part string `yaml:"part,omitempty"`
// description: |
// Negative specifies if the match should be reversed
// It will only match if the condition is not true.
Negative bool `yaml:"negative,omitempty"`
// Name is matcher Name
// description: |
// Name of the matcher. Name should be lowercase and must not contain
// spaces or dashes (-).
// examples:
// - value: "\"cookie-matcher\""
Name string `yaml:"name,omitempty"`
// Status are the acceptable status codes for the response
// description: |
// Status are the acceptable status codes for the response.
// examples:
// - value: >
// []int{200, 302}
Status []int `yaml:"status,omitempty"`
// description: |
// Size is the acceptable size for the response
// examples:
// - value: >
// []int{3029, 2042}
Size []int `yaml:"size,omitempty"`
// Words are the words required to be present in the response
// description: |
// Words contains word patterns required to be present in the response part.
// examples:
// - name: Match for outlook mail protection domain
// value: >
// []string{"mail.protection.outlook.com"}
// - name: Match for application/json in response headers
// value: >
// []string{"application/json"}
Words []string `yaml:"words,omitempty"`
// Regex are the regex pattern required to be present in the response
// description: |
// Regex contains Regular Expression patterns required to be present in the response part.
// examples:
// - name: Match for Linkerd Service via Regex
// value: >
// []string{`(?mi)^Via\\s*?:.*?linkerd.*$`}
// - name: Match for Open Redirect via Location header
// value: >
// []string{`(?m)^(?:Location\\s*?:\\s*?)(?:https?://|//)?(?:[a-zA-Z0-9\\-_\\.@]*)example\\.com.*$`}
Regex []string `yaml:"regex,omitempty"`
// Binary are the binary characters required to be present in the response
// description: |
// Binary are the binary patterns required to be present in the response part.
// examples:
// - name: Match for Springboot Heapdump Actuator "JAVA PROFILE", "HPROF", "Gunzip magic byte"
// value: >
// []string{"4a4156412050524f46494c45", "4850524f46", "1f8b080000000000"}
// - name: Match for 7zip files
// value: >
// []string{"377ABCAF271C"}
Binary []string `yaml:"binary,omitempty"`
// DSL are the dsl queries
// description: |
// DSL are the dsl expressions that will be evaluated as part of nuclei matching rules.
// A list of these helper functions are available [here](https://nuclei.projectdiscovery.io/templating-guide/helper-functions/).
// examples:
// - name: DSL Matcher for package.json file
// value: >
// []string{"contains(body, 'packages') && contains(tolower(all_headers), 'application/octet-stream') && status_code == 200"}
// - name: DSL Matcher for missing strict transport security header
// value: >
// []string{"!contains(tolower(all_headers), ''strict-transport-security'')"}
DSL []string `yaml:"dsl,omitempty"`
// Encoding specifies the encoding for the word content if any.
// description: |
// Encoding specifies the encoding for the words field if any.
// values:
// - "hex"
Encoding string `yaml:"encoding,omitempty"`
// cached data for the compiled matcher

View File

@ -8,14 +8,23 @@ import (
// Operators contains the operators that can be applied on protocols
type Operators struct {
// description: |
// Matchers contains the detection mechanism for the request to identify
// whether the request was successful
// whether the request was successful by doing pattern matching
// on request/responses.
//
// Multiple matchers can be combined together with `matcher-condition` flag
// which accepts either `and` or `or` as argument.
Matchers []*matchers.Matcher `yaml:"matchers,omitempty"`
// description: |
// Extractors contains the extraction mechanism for the request to identify
// and extract parts of the response.
Extractors []*extractors.Extractor `yaml:"extractors,omitempty"`
// MatchersCondition is the condition of the matchers
// whether to use AND or OR. Default is OR.
// description: |
// MatchersCondition is the condition between the matchers. Default is OR.
// values:
// - "and"
// - "or"
MatchersCondition string `yaml:"matchers-condition,omitempty"`
// cached variables that may be used along with request.
matchersCondition matchers.ConditionType

View File

@ -20,13 +20,42 @@ type Request struct {
ID string `yaml:"id"`
// Path contains the path/s for the request
// description: |
// Name is the Hostname to make DNS request for.
//
// Generally, it is set to {{FQDN}} which is the domain we get from input.
// examples:
// - value: "\"{{FQDN}}\""
Name string `yaml:"name"`
// Type is the type of DNS request to make
// description: |
// Type is the type of DNS request to make.
// values:
// - "A"
// - "NS"
// - "CNAME"
// - "SOA"
// - "PTR"
// - "MX"
// - "TXT"
// - "AAAA"
Type string `yaml:"type"`
// Class is the class of the DNS request
// description: |
// Class is the class of the DNS request.
//
// Usually it's enough to just leave it as INET.
// values:
// - "INET"
// - "CSNET"
// - "CHAOS"
// - "HESIOD"
// - "NONE"
// - "ANY"
Class string `yaml:"class"`
// description: |
// Retries is the number of retries for the DNS request
// examples:
// - name: Use a retry of 3 to 5 generally
// value: 5
Retries int `yaml:"retries"`
CompiledOperators *operators.Operators
@ -37,7 +66,8 @@ type Request struct {
class uint16
question uint16
// Recursion specifies whether to recurse all the answers.
// description: |
// Recursion determines if resolver should recurse all records to get fresh results.
Recursion bool `yaml:"recursion"`
}

View File

@ -12,16 +12,29 @@ import (
type Request struct {
// Operators for the current request go here.
operators.Operators `yaml:",inline"`
// description: |
// Extensions is the list of extensions to perform matching on.
// examples:
// - value: '[]string{".txt", ".go", ".json"}'
Extensions []string `yaml:"extensions"`
// description: |
// ExtensionDenylist is the list of file extensions to deny during matching.
//
// By default, it contains some non-interesting extensions that are hardcoded
// in nuclei.
// examples:
// - value: '[]string{".avi", ".mov", ".mp3"}'
ExtensionDenylist []string `yaml:"denylist"`
ID string `yaml:"id"`
// description: |
// MaxSize is the maximum size of the file to run request on.
//
// By default, nuclei will process 5MB files and not go more than that.
// It can be set to much lower or higher depending on use.
// examples:
// - value: 2048
MaxSize int `yaml:"max-size"`
CompiledOperators *operators.Operators
@ -30,6 +43,7 @@ type Request struct {
extensions map[string]struct{}
extensionDenylist map[string]struct{}
// description: |
// NoRecursive specifies whether to not do recursive checks if folders are provided.
NoRecursive bool `yaml:"no-recursive"`

View File

@ -109,9 +109,45 @@ var ActionToActionString = map[ActionType]string{
// are discovered on the found page. We also keep track and only
// scrape new navigation from pages we haven't crawled yet.
type Action struct {
// description:
// Args contain arguments for the headless action.
//
// Per action arguments are described in detail [here](https://nuclei.projectdiscovery.io/templating-guide/protocols/headless/).
Data map[string]string `yaml:"args,omitempty"`
// description: |
// Name is the name assigned to the headless action.
//
// This can be used to execute code, for instance in browser
// DOM using script action, and get the result in a variable
// which can be matched upon by nuclei. An Example template [here](https://github.com/projectdiscovery/nuclei-templates/blob/master/headless/prototype-pollution-check.yaml).
Name string `yaml:"name,omitempty"`
// description: |
// Description is the optional description of the headless action
Description string `yaml:"description,omitempty"`
// description: |
// Action is the type of the action to perform.
// values:
// - "navigate"
// - "script"
// - "click"
// - "rightclick"
// - "text"
// - "screenshot"
// - "time"
// - "select"
// - "files"
// - "waitload"
// - "getresource"
// - "extract"
// - "setmethod"
// - "addheader"
// - "setheader"
// - "deleteheader"
// - "setbody"
// - "waitevent"
// - "keyboard"
// - "debug"
// - "sleep"
ActionType string `yaml:"action"`
}

View File

@ -11,6 +11,7 @@ import (
type Request struct {
ID string `yaml:"id"`
// description: |
// Steps is the list of actions to run for headless request
Steps []*engine.Action `yaml:"steps"`

View File

@ -16,36 +16,110 @@ import (
type Request struct {
// Operators for the current request go here.
operators.Operators `yaml:",inline"`
// Path contains the path/s for the request
// description: |
// Path contains the path/s for the HTTP requests. It supports variables
// as placeholders.
// examples:
// - name: Some example path values
// value: >
// []string{"{{BaseURL}}", "{{BaseURL}}/+CSCOU+/../+CSCOE+/files/file_list.json?path=/sessions"}
Path []string `yaml:"path"`
// Raw contains raw requests
// description: |
// Raw contains HTTP Requests in Raw format.
// examples:
// - name: Some example raw requests
// value: |
// []string{"GET /etc/passwd HTTP/1.1\nHost:\nContent-Length: 4", "POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.1\nHost: {{Hostname}}\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0\nContent-Length: 1\nConnection: close\n\necho\necho\ncat /etc/passwd 2>&1"}
Raw []string `yaml:"raw"`
ID string `yaml:"id"`
// Name is the name of the request
// docgen:nodoc
ID string `yaml:"-"`
// description: |
// 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 multirequest based matchers.
Name string `yaml:"Name"`
// AttackType is the attack type
// Sniper, PitchFork and ClusterBomb. Default is Sniper
// description: |
// Attack is the type of payload combinations to perform.
//
// Sniper is each payload once, pitchfork combines multiple payload sets and clusterbomb generates
// permutations and combinations for all payloads.
// values:
// - "sniper"
// - "pitchfork"
// - "clusterbomb"
AttackType string `yaml:"attack"`
// Method is the request method, whether GET, POST, PUT, etc
// description: |
// Method is the HTTP Request Method.
// values:
// - "GET"
// - "POST"
// - "PUT"
// - "DELETE"
Method string `yaml:"method"`
// Body is an optional parameter which contains the request body for POST methods, etc
// description: |
// Body is an optional parameter which contains HTTP Request body.
// examples:
// - name: Same Body for a Login POST request
// value: "\"username=test&password=test\""
Body string `yaml:"body"`
// Path contains the path/s for the request variables
// description: |
// Payloads contains any payloads for the current request.
//
// 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.
// examples:
// - name: A payload list for Tomcat Bruteforce
// value: exampleTomcatUserPassPayload
// - name: A payload example of reading from file
// value: exampleFileBasedPayload
Payloads map[string]interface{} `yaml:"payloads"`
// Headers contains headers to send with the 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"`
// RaceNumberRequests is the number of same request to send in race condition attack
// description: |
// RaceCount is the number of times to send a request in Race Condition Attack.
// examples:
// - name: Send a request 5 times
// value: "5"
RaceNumberRequests int `yaml:"race_count"`
// description: |
// MaxRedirects is the maximum number of redirects that should be followed.
// examples:
// - name: Follow upto 5 redirects
// value: "5"
MaxRedirects int `yaml:"max-redirects"`
// PipelineConcurrentConnections is number of connections in pipelining
// description: |
// PipelineConcurrentConnections is number of connections to create during pipelining.
// examples:
// - name: Create 40 concurrent connections
// value: 40
PipelineConcurrentConnections int `yaml:"pipeline-concurrent-connections"`
// PipelineRequestsPerConnection is number of requests in pipelining
// description: |
// PipelineRequestsPerConnection is number of requests to send per connection when pipelining.
// examples:
// - name: Send 100 requests per pipeline connection
// value: 100
PipelineRequestsPerConnection int `yaml:"pipeline-requests-per-connection"`
// Threads specifies number of threads for sending requests
// description: |
// Threads specifies number of threads to use sending requests. This enables Connection Pooling.
//
// Connection: Close attribute must not be used in request while using threads flag, otherwise
// pooling will fail and engine will continue to close connections after requests.
// examples:
// - name: Send requests using 10 concurrent threads
// value: 10
Threads int `yaml:"threads"`
// description: |
// MaxSize is the maximum size of http response body to read in bytes.
// examples:
// - name: Read max 2048 bytes of the response
// value: 2048
MaxSize int `yaml:"max-size"`
CompiledOperators *operators.Operators
@ -57,21 +131,36 @@ type Request struct {
generator *generators.Generator // optional, only enabled when using payloads
httpClient *retryablehttp.Client
rawhttpClient *rawhttp.Client
// CookieReuse is an optional setting that makes cookies shared within requests
// description: |
// CookieReuse is an optional setting that enables cookie reuse for
// all requests defined in raw section.
CookieReuse bool `yaml:"cookie-reuse"`
// Redirects specifies whether redirects should be followed.
// description: |
// Redirects specifies whether redirects should be followed by the HTTP Client.
//
// This can be used in conjunction with `max-redirects` to control the HTTP request redirects.
Redirects bool `yaml:"redirects"`
// Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining (race conditions/billions requests)
// All requests must be indempotent (GET/POST)
// description: |
// Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining
//
// All requests must be indempotent (GET/POST). This can be used for race conditions/billions requests.
Pipeline bool `yaml:"pipeline"`
// Specify in order to skip request RFC normalization
// description: |
// 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.
Unsafe bool `yaml:"unsafe"`
// Race determines if all the request have to be attempted at the same time
// The minimum number of requests is determined by threads
// description: |
// 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.
Race bool `yaml:"race"`
// ReqCondition automatically assigns numbers to requests and preserves
// their history for being matched at the end.
// Currently only works with sequential http requests.
// description: |
// ReqCondition automatically assigns numbers to requests and preserves their history.
//
// This allows matching on them later for multi-request conditions.
ReqCondition bool `yaml:"req-condition"`
}

View File

@ -14,7 +14,6 @@ func TestHTTPCompile(t *testing.T) {
testutils.Init(options)
templateID := "testing-http"
request := &Request{
ID: templateID,
Name: "testing",
Payloads: map[string]interface{}{
"username": []string{"admin"},

View File

@ -17,19 +17,44 @@ import (
type Request struct {
ID string `yaml:"id"`
// Address is the address to send requests to (host:port:tls combos generally)
// description: |
// Address is the address to send requests to.
//
// Usually it's set to `{{Hostname}}`. If you want to enable TLS for
// TCP Connection, you can use `tls://{{Hostname}}`.
// examples:
// - value: |
// []string{"{{Hostname}}"}
Address []string `yaml:"host"`
addresses []addressKV
// AttackType is the attack type
// Sniper, PitchFork and ClusterBomb. Default is Sniper
// description: |
// Attack is the type of payload combinations to perform.
//
// Sniper is each payload once, pitchfork combines multiple payload sets and clusterbomb generates
// permutations and combinations for all payloads.
// values:
// - "sniper"
// - "pitchfork"
// - "clusterbomb"
AttackType string `yaml:"attack"`
// Path contains the path/s for the request variables
// description: |
// Payloads contains any payloads for the current request.
//
// 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"`
// Payload is the payload to send for the network request
// description: |
// Inputs contains inputs for the network socket
Inputs []*Input `yaml:"inputs"`
// ReadSize is the size of response to read (1024 if not provided by default)
// description: |
// ReadSize is the size of response to read at the end
//
// Default value for read-size is 1024.
// examples:
// - value: "2048"
ReadSize int `yaml:"read-size"`
// Operators for the current request go here.
@ -51,13 +76,37 @@ type addressKV struct {
// Input is the input to send on the network
type Input struct {
// Data is the data to send as the input
// description: |
// Data is the data to send as the input.
//
// It supports DSL Helper Functions as well as normal expressions.
// examples:
// - value: "\"TEST\""
// - value: "\"hex_decode('50494e47')\""
Data string `yaml:"data"`
// Type is the type of input - hex, text.
// description: |
// Type is the type of input specified in `data` field.
//
// Default value is text, but hex can be used for hex formatted data.
// values:
// - "hex"
// - "text"
Type string `yaml:"type"`
// Read is the number of bytes to read from socket
// description: |
// Read is the number of bytes to read from socket.
//
// This can be used for protcols which expected an immediate response. You can
// read and write responses one after another and evetually perform matching
// on every data captured with `name` attribute.
//
// The [network docs](https://nuclei.projectdiscovery.io/templating-guide/protocols/network/) highlight more on how to do this.
// examples:
// - value: "1024"
Read int `yaml:"read"`
// Name is the optional name of the input to provide matching on
// description: |
// Name is the optional name of the data read to provide matching on.
// examples:
// - value: "\"prefix\""
Name string `yaml:"name"`
}

View File

@ -10,23 +10,55 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/workflows"
)
// Template is a request template parsed from a yaml file
var (
exampleTomcatUserPassPayload = map[string]interface{}{
"username": []string{"tomcat", "admin"},
"password": []string{"tomcat", "admin", "password"},
}
exampleFileBasedPayload = map[string]interface{}{
"data": "helpers/payloads/command-injection.txt",
}
)
// Template is a YAML input file which defines the requests and
// others metadata for a scan template.
type Template struct {
// ID is the unique id for the template
// description: |
// ID is the unique id for the template. IDs must be lowercase
// and must not contain spaces in it.
//
// #### Good IDs
//
// A good ID unqiuely 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`.
// examples:
// - name: ID Example
// value: "\"cve-2021-19520\""
ID string `yaml:"id"`
// Info contains information about the template
// description: |
// Info contains metadata information about the template. At minimum, it
// should contain `name`, `author`, `severity`, `description`, `tags`. Optionally
// you can also specify a list of `references` for the template.
Info map[string]interface{} `yaml:"info"`
// RequestsHTTP contains the http request to make in the template
// description: |
// Requests contains the http request to make in the template
RequestsHTTP []*http.Request `yaml:"requests,omitempty" json:"requests"`
// RequestsDNS contains the dns request to make in the template
// description: |
// DNS contains the dns request to make in the template
RequestsDNS []*dns.Request `yaml:"dns,omitempty" json:"dns"`
// RequestsFile contains the file request to make in the template
// description: |
// File contains the file request to make in the template
RequestsFile []*file.Request `yaml:"file,omitempty" json:"file"`
// RequestsNetwork contains the network request to make in the template
// description: |
// Network contains the network request to make in the template
RequestsNetwork []*network.Request `yaml:"network,omitempty" json:"network"`
// RequestsHeadless contains the headless request to make in the template.
// description: |
// Headless contains the headless request to make in the template.
RequestsHeadless []*headless.Request `yaml:"headless,omitempty" json:"headless"`
// description: |
// Workflows is a yaml based workflow declaration code.
workflows.Workflow `yaml:",inline,omitempty"`
CompiledWorkflow *workflows.Workflow `yaml:"-" json:"-" jsonschema:"-"`

View File

@ -4,7 +4,8 @@ import "github.com/projectdiscovery/nuclei/v2/pkg/protocols"
// Workflow is a workflow to execute with chained requests, etc.
type Workflow struct {
// Workflows is a yaml based workflow declaration code.
// description: |
// Workflows is a list of workflows to execute for a template.
Workflows []*WorkflowTemplate `yaml:"workflows,omitempty"`
Options *protocols.ExecuterOptions
@ -12,13 +13,22 @@ type Workflow struct {
// WorkflowTemplate is a template to be ran as part of a workflow
type WorkflowTemplate struct {
// Template is the template to run
// description: |
// Template is a single template or directory to execute as part of workflow.
// examples:
// - name: A single template
// value: "\"dns/worksites-detection.yaml\""
// - name: A template directory
// value: "\"misconfigurations/aem\""
Template string `yaml:"template"`
// Tags to perform filtering of supplied templates on
// description: |
// Tags to run templates based on.
Tags string `yaml:"tags"`
// description: |
// Matchers perform name based matching to run subtemplates for a workflow.
Matchers []*Matcher `yaml:"matchers"`
// Subtemplates are ran if the template matches.
// description: |
// Subtemplates are ran if the `template` field Template matches.
Subtemplates []*WorkflowTemplate `yaml:"subtemplates"`
// Executers perform the actual execution for the workflow template
Executers []*ProtocolExecuterPair
@ -32,8 +42,10 @@ type ProtocolExecuterPair struct {
// Matcher performs conditional matching on the workflow template results.
type Matcher struct {
// description: |
// Name is the name of the item to match.
Name string `yaml:"name"`
// description: |
// Subtemplates are ran if the name of matcher matches.
Subtemplates []*WorkflowTemplate `yaml:"subtemplates"`
}