2020-12-21 14:31:32 +05:30
package operators
import (
2020-12-25 02:24:55 +05:30
"github.com/pkg/errors"
2021-07-19 21:04:08 +03:00
2020-12-21 14:31:32 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors"
"github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers"
)
// Operators contains the operators that can be applied on protocols
type Operators struct {
2021-07-27 16:03:56 +05:30
// description: |
// Matchers contains the detection mechanism for the request to identify
// whether the request was successful by doing pattern matching
// on request/responses.
//
2021-09-07 17:31:46 +03:00
// Multiple matchers can be combined with `matcher-condition` flag
2021-07-27 16:03:56 +05:30
// which accepts either `and` or `or` as argument.
2021-08-23 23:50:45 +05:30
Matchers [ ] * matchers . Matcher ` yaml:"matchers,omitempty" jsonschema:"title=matchers to run on response,description=Detection mechanism to identify whether the request was successful by doing pattern matching" `
2021-07-27 16:03:56 +05:30
// description: |
// Extractors contains the extraction mechanism for the request to identify
// and extract parts of the response.
2021-08-23 23:50:45 +05:30
Extractors [ ] * extractors . Extractor ` yaml:"extractors,omitempty" jsonschema:"title=extractors to run on response,description=Extractors contains the extraction mechanism for the request to identify and extract parts of the response" `
2021-07-27 16:03:56 +05:30
// description: |
// MatchersCondition is the condition between the matchers. Default is OR.
// values:
// - "and"
// - "or"
2021-08-23 23:50:45 +05:30
MatchersCondition string ` yaml:"matchers-condition,omitempty" jsonschema:"title=condition between the matchers,description=Conditions between the matchers,enum=and,enum=or" `
2020-12-21 14:31:32 +05:30
// cached variables that may be used along with request.
matchersCondition matchers . ConditionType
}
2020-12-25 02:24:55 +05:30
// Compile compiles the operators as well as their corresponding matchers and extractors
func ( r * Operators ) Compile ( ) error {
if r . MatchersCondition != "" {
r . matchersCondition = matchers . ConditionTypes [ r . MatchersCondition ]
} else {
2020-12-30 13:26:55 +05:30
r . matchersCondition = matchers . ORCondition
2020-12-25 02:24:55 +05:30
}
for _ , matcher := range r . Matchers {
if err := matcher . CompileMatchers ( ) ; err != nil {
return errors . Wrap ( err , "could not compile matcher" )
}
}
for _ , extractor := range r . Extractors {
if err := extractor . CompileExtractors ( ) ; err != nil {
return errors . Wrap ( err , "could not compile extractor" )
}
}
return nil
}
2020-12-21 14:31:32 +05:30
// GetMatchersCondition returns the condition for the matchers
func ( r * Operators ) GetMatchersCondition ( ) matchers . ConditionType {
return r . matchersCondition
}
2020-12-24 12:56:28 +05:30
// Result is a result structure created from operators running on data.
type Result struct {
2021-02-21 16:31:34 +05:30
// Matched is true if any matchers matched
Matched bool
// Extracted is true if any result type values were extracted
Extracted bool
2020-12-24 12:56:28 +05:30
// Matches is a map of matcher names that we matched
Matches map [ string ] struct { }
// Extracts contains all the data extracted from inputs
Extracts map [ string ] [ ] string
2020-12-24 20:47:41 +05:30
// OutputExtracts is the list of extracts to be displayed on screen.
OutputExtracts [ ] string
2020-12-24 12:56:28 +05:30
// DynamicValues contains any dynamic values to be templated
2021-01-01 16:52:41 +05:30
DynamicValues map [ string ] interface { }
2020-12-29 01:30:07 +05:30
// PayloadValues contains payload values provided by user. (Optional)
PayloadValues map [ string ] interface { }
2020-12-24 12:56:28 +05:30
}
2021-04-18 16:10:10 +05:30
// Merge merges a result structure into the other.
func ( r * Result ) Merge ( result * Result ) {
if ! r . Matched && result . Matched {
r . Matched = result . Matched
}
if ! r . Extracted && result . Extracted {
r . Extracted = result . Extracted
}
for k , v := range result . Matches {
r . Matches [ k ] = v
}
for k , v := range result . Extracts {
r . Extracts [ k ] = v
}
2021-05-01 18:28:24 +05:30
r . OutputExtracts = append ( r . OutputExtracts , result . OutputExtracts ... )
2021-04-18 16:10:10 +05:30
for k , v := range result . DynamicValues {
r . DynamicValues [ k ] = v
}
for k , v := range result . PayloadValues {
r . PayloadValues [ k ] = v
}
}
2020-12-24 20:47:41 +05:30
// MatchFunc performs matching operation for a matcher on model and returns true or false.
2021-09-29 19:43:46 +03:00
type MatchFunc func ( data map [ string ] interface { } , matcher * matchers . Matcher ) ( bool , [ ] string )
2020-12-24 20:47:41 +05:30
2021-09-07 17:31:46 +03:00
// ExtractFunc performs extracting operation for an extractor on model and returns true or false.
2020-12-24 20:47:41 +05:30
type ExtractFunc func ( data map [ string ] interface { } , matcher * extractors . Extractor ) map [ string ] struct { }
2020-12-24 12:56:28 +05:30
// Execute executes the operators on data and returns a result structure
2020-12-25 12:55:46 +05:30
func ( r * Operators ) Execute ( data map [ string ] interface { } , match MatchFunc , extract ExtractFunc ) ( * Result , bool ) {
2020-12-24 12:56:28 +05:30
matcherCondition := r . GetMatchersCondition ( )
2020-12-30 13:26:55 +05:30
var matches bool
2020-12-24 12:56:28 +05:30
result := & Result {
Matches : make ( map [ string ] struct { } ) ,
Extracts : make ( map [ string ] [ ] string ) ,
2021-01-01 16:52:41 +05:30
DynamicValues : make ( map [ string ] interface { } ) ,
2020-12-24 12:56:28 +05:30
}
2021-04-18 16:10:10 +05:30
2021-03-09 16:35:53 +05:30
// Start with the extractors first and evaluate them.
2020-12-24 12:56:28 +05:30
for _ , extractor := range r . Extractors {
2020-12-24 20:47:41 +05:30
var extractorResults [ ] string
for match := range extract ( data , extractor ) {
2020-12-24 12:56:28 +05:30
extractorResults = append ( extractorResults , match )
if extractor . Internal {
if _ , ok := result . DynamicValues [ extractor . Name ] ; ! ok {
result . DynamicValues [ extractor . Name ] = match
}
} else {
2020-12-24 20:47:41 +05:30
result . OutputExtracts = append ( result . OutputExtracts , match )
2020-12-24 12:56:28 +05:30
}
}
2021-01-11 21:11:35 +05:30
if len ( extractorResults ) > 0 && ! extractor . Internal && extractor . Name != "" {
2020-12-30 13:26:55 +05:30
result . Extracts [ extractor . Name ] = extractorResults
}
2020-12-24 12:56:28 +05:30
}
2021-03-09 16:35:53 +05:30
for _ , matcher := range r . Matchers {
// Check if the matcher matched
2021-09-29 19:43:46 +03:00
if isMatch , _ := match ( data , matcher ) ; isMatch {
2021-09-07 17:31:46 +03:00
// If the matcher has matched, and it's an OR
2021-03-09 16:35:53 +05:30
// write the first output then move to next matcher.
if matcherCondition == matchers . ORCondition && matcher . Name != "" {
result . Matches [ matcher . Name ] = struct { } { }
}
2021-09-29 19:43:46 +03:00
2021-03-09 16:35:53 +05:30
matches = true
2021-09-29 19:43:46 +03:00
} else if matcherCondition == matchers . ANDCondition {
if len ( result . DynamicValues ) > 0 {
return result , true
}
return nil , false
2021-03-09 16:35:53 +05:30
}
}
2021-02-21 16:31:34 +05:30
result . Matched = matches
result . Extracted = len ( result . OutputExtracts ) > 0
if len ( result . DynamicValues ) > 0 {
return result , true
}
2021-09-16 20:35:43 +03:00
2021-09-29 19:43:46 +03:00
// Don't print if we have matchers, and they have not matched, regardless of extractor
2020-12-30 13:26:55 +05:30
if len ( r . Matchers ) > 0 && ! matches {
return nil , false
}
2020-12-24 12:56:28 +05:30
// Write a final string of output if matcher type is
// AND or if we have extractors for the mechanism too.
2021-02-21 16:31:34 +05:30
if len ( result . Extracts ) > 0 || len ( result . OutputExtracts ) > 0 || matches {
2020-12-24 12:56:28 +05:30
return result , true
}
return nil , false
}
2021-07-06 18:34:25 +05:30
// ExecuteInternalExtractors executes internal dynamic extractors
func ( r * Operators ) ExecuteInternalExtractors ( data map [ string ] interface { } , extract ExtractFunc ) map [ string ] interface { } {
dynamicValues := make ( map [ string ] interface { } )
// Start with the extractors first and evaluate them.
for _ , extractor := range r . Extractors {
if ! extractor . Internal {
continue
}
for match := range extract ( data , extractor ) {
if _ , ok := dynamicValues [ extractor . Name ] ; ! ok {
dynamicValues [ extractor . Name ] = match
}
}
}
return dynamicValues
}