nuclei/pkg/operators/operators.go

412 lines
13 KiB
Go
Raw Normal View History

package operators
import (
"fmt"
"maps"
"strconv"
2022-11-25 17:22:50 +01:00
"strings"
"github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/excludematchers"
sliceutil "github.com/projectdiscovery/utils/slice"
)
// 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.
2023-02-07 16:10:40 +08:00
Matchers []*matchers.Matcher `yaml:"matchers,omitempty" json:"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.
2023-02-07 16:10:40 +08:00
Extractors []*extractors.Extractor `yaml:"extractors,omitempty" json:"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"
2023-02-07 16:10:40 +08:00
MatchersCondition string `yaml:"matchers-condition,omitempty" json:"matchers-condition,omitempty" jsonschema:"title=condition between the matchers,description=Conditions between the matchers,enum=and,enum=or"`
// cached variables that may be used along with request.
matchersCondition matchers.ConditionType
// TemplateID is the ID of the template for matcher
TemplateID string `json:"-" yaml:"-" jsonschema:"-"`
// ExcludeMatchers is a list of excludeMatchers items
ExcludeMatchers *excludematchers.ExcludeMatchers `json:"-" yaml:"-" jsonschema:"-"`
}
// Compile compiles the operators as well as their corresponding matchers and extractors
func (operators *Operators) Compile() error {
if operators.MatchersCondition != "" {
operators.matchersCondition = matchers.ConditionTypes[operators.MatchersCondition]
} else {
operators.matchersCondition = matchers.ORCondition
}
for _, matcher := range operators.Matchers {
if err := matcher.CompileMatchers(); err != nil {
return errors.Wrap(err, "could not compile matcher")
}
}
for _, extractor := range operators.Extractors {
if err := extractor.CompileExtractors(); err != nil {
return errors.Wrap(err, "could not compile extractor")
}
}
return nil
}
func (operators *Operators) HasDSL() bool {
for _, matcher := range operators.Matchers {
if len(matcher.DSL) > 0 {
return true
}
}
for _, extractor := range operators.Extractors {
if len(extractor.DSL) > 0 {
return true
}
}
return false
}
// GetMatchersCondition returns the condition for the matchers
func (operators *Operators) GetMatchersCondition() matchers.ConditionType {
return operators.matchersCondition
}
2020-12-24 12:56:28 +05:30
// Result is a result structure created from operators running on data.
type Result struct {
// 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][]string
2020-12-24 12:56:28 +05:30
// 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
outputUnique map[string]struct{}
2020-12-24 12:56:28 +05:30
// DynamicValues contains any dynamic values to be templated
DynamicValues map[string][]string
// PayloadValues contains payload values provided by user. (Optional)
PayloadValues map[string]interface{}
// Optional lineCounts for file protocol
LineCount string
// Operators is reference to operators that generated this result (Read-Only)
Operators *Operators
2020-12-24 12:56:28 +05:30
}
2022-11-25 17:22:50 +01:00
func (result *Result) HasMatch(name string) bool {
return result.hasItem(name, result.Matches)
}
func (result *Result) HasExtract(name string) bool {
return result.hasItem(name, result.Extracts)
}
func (result *Result) hasItem(name string, m map[string][]string) bool {
for matchName := range m {
if strings.EqualFold(name, matchName) {
return true
}
}
return false
}
// MakeDynamicValuesCallback takes an input dynamic values map and calls
// the callback function with all variations of the data in input in form
// of map[string]string (interface{}).
func MakeDynamicValuesCallback(input map[string][]string, iterateAllValues bool, callback func(map[string]interface{}) bool) {
output := make(map[string]interface{}, len(input))
if !iterateAllValues {
for k, v := range input {
if len(v) > 0 {
output[k] = v[0]
}
}
callback(output)
return
}
inputIndex := make(map[string]int, len(input))
var maxValue int
for _, v := range input {
if len(v) > maxValue {
maxValue = len(v)
}
}
for i := 0; i < maxValue; i++ {
for k, v := range input {
if len(v) == 0 {
continue
}
if len(v) == 1 {
output[k] = v[0]
continue
}
if gotIndex, ok := inputIndex[k]; !ok {
inputIndex[k] = 0
output[k] = v[0]
} else {
newIndex := gotIndex + 1
if newIndex >= len(v) {
output[k] = v[len(v)-1]
continue
}
output[k] = v[newIndex]
inputIndex[k] = newIndex
}
}
// skip if the callback says so
if callback(output) {
return
}
}
}
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 {
2022-03-02 08:31:23 +01:00
r.Matches[k] = sliceutil.Dedupe(append(r.Matches[k], v...))
2021-04-18 16:10:10 +05:30
}
for k, v := range result.Extracts {
2022-03-02 08:31:23 +01:00
r.Extracts[k] = sliceutil.Dedupe(append(r.Extracts[k], v...))
2021-04-18 16:10:10 +05:30
}
r.outputUnique = make(map[string]struct{})
output := r.OutputExtracts
r.OutputExtracts = make([]string, 0, len(output))
for _, v := range output {
if _, ok := r.outputUnique[v]; !ok {
r.outputUnique[v] = struct{}{}
r.OutputExtracts = append(r.OutputExtracts, v)
}
}
for _, v := range result.OutputExtracts {
if _, ok := r.outputUnique[v]; !ok {
r.outputUnique[v] = struct{}{}
r.OutputExtracts = append(r.OutputExtracts, v)
}
}
2021-04-18 16:10:10 +05:30
for k, v := range result.DynamicValues {
if _, ok := r.DynamicValues[k]; !ok {
r.DynamicValues[k] = v
} else {
r.DynamicValues[k] = sliceutil.Dedupe(append(r.DynamicValues[k], v...))
}
2021-04-18 16:10:10 +05:30
}
maps.Copy(r.PayloadValues, result.PayloadValues)
2021-04-18 16:10:10 +05:30
}
2020-12-24 20:47:41 +05:30
// MatchFunc performs matching operation for a matcher on model and returns true or false.
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
func (operators *Operators) Execute(data map[string]interface{}, match MatchFunc, extract ExtractFunc, isDebug bool) (*Result, bool) {
matcherCondition := operators.GetMatchersCondition()
2020-12-24 12:56:28 +05:30
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][]string),
2020-12-24 12:56:28 +05:30
Extracts: make(map[string][]string),
DynamicValues: make(map[string][]string),
outputUnique: make(map[string]struct{}),
Operators: operators,
2020-12-24 12:56:28 +05:30
}
2021-04-18 16:10:10 +05:30
// state variable to check if all extractors are internal
var allInternalExtractors = true
2021-03-09 16:35:53 +05:30
// Start with the extractors first and evaluate them.
for _, extractor := range operators.Extractors {
if !extractor.Internal && allInternalExtractors {
allInternalExtractors = false
}
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 data, ok := result.DynamicValues[extractor.Name]; !ok {
result.DynamicValues[extractor.Name] = []string{match}
} else {
result.DynamicValues[extractor.Name] = append(data, match)
2020-12-24 12:56:28 +05:30
}
} else {
if _, ok := result.outputUnique[match]; !ok {
result.OutputExtracts = append(result.OutputExtracts, match)
result.outputUnique[match] = struct{}{}
}
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
}
// update data with whatever was extracted doesn't matter if it is internal or not (skip unless it empty)
if len(extractorResults) > 0 {
data[extractor.Name] = getExtractedValue(extractorResults)
}
2020-12-24 12:56:28 +05:30
}
// expose dynamic values to same request matchers
if len(result.DynamicValues) > 0 {
dataDynamicValues := make(map[string]interface{})
for dynName, dynValues := range result.DynamicValues {
if len(dynValues) > 1 {
for dynIndex, dynValue := range dynValues {
dynKeyName := fmt.Sprintf("%s%d", dynName, dynIndex)
dataDynamicValues[dynKeyName] = dynValue
}
dataDynamicValues[dynName] = dynValues
} else {
dataDynamicValues[dynName] = dynValues[0]
}
}
data = generators.MergeMaps(data, dataDynamicValues)
}
for matcherIndex, matcher := range operators.Matchers {
// Skip matchers that are in the blocklist
if operators.ExcludeMatchers != nil {
if operators.ExcludeMatchers.Match(operators.TemplateID, matcher.Name) {
continue
}
}
if isMatch, matched := match(data, matcher); isMatch {
if isDebug { // matchers without an explicit name or with AND condition should only be made visible if debug is enabled
Fuzzing layer enhancements + input-types support (#4477) * feat: move fuzz package to root directory * feat: added support for input providers like openapi,postman,etc * feat: integration of new fuzzing logic in engine * bugfix: use and instead of or * fixed lint errors * go mod tidy * add new reqresp type + bump utils * custom http request parser * use new struct type RequestResponse * introduce unified input/target provider * abstract input formats via new inputprovider * completed input provider refactor * remove duplicated code * add sdk method to load targets * rename component url->path * add new yaml format + remove duplicated code * use gopkg.in/yaml.v3 for parsing * update .gitignore * refactor/move + docs fuzzing in http protocol * fuzz: header + query integration test using fuzzplayground * fix integration test runner in windows * feat add support for filter in http fuzz * rewrite header/query integration test with filter * add replace regex rule * support kv fuzzing + misc updates * add path fuzzing example + misc improvements * fix matchedURL + skip httpx on multi formats * cookie fuzz integration test * add json body + params body tests * feat add multipart/form-data fuzzing support * add all fuzz body integration test * misc bug fixes + minor refactor * add multipart form + body form unit tests * only run fuzzing templates if -fuzz flag is given * refactor/move fuzz playground server to pkg * fix integration test + refactor * add auth types and strategies * add file auth provider * start implementing auth logic in http * add logic in http protocol * static auth implemented for http * default :80,:443 normalization * feat: dynamic auth init * feat: dynamic auth using templates * validate targets count in openapi+swagger * inputformats: add support to accept variables * fix workflow integration test * update lazy cred fetch logic * fix unit test * drop postman support * domain related normalization * update secrets.yaml file format + misc updates * add auth prefetch option * remove old secret files * add fuzzing+auth related sdk options * fix/support multiple mode in kv header fuzzing * rename 'headers' -> 'header' in fuzzing rules * fix deadlock due to merge conflict resolution * misc update * add bool type in parsed value * add openapi validation+override+ new flags * misc updates * remove optional path parameters when unavailable * fix swagger.yaml file * misc updates * update print msg * multiple openapi validation enchancements + appMode * add optional params in required_openapi_vars.yaml file * improve warning/verbose msgs in format * fix skip-format-validation not working * use 'params/parameter' instead of 'variable' in openapi * add retry support for falky tests * fix nuclei loading ignored templates (#4849) * fix tag include logic * fix unit test * remove quoting in extractor output * remove quote in debug code command * feat: issue tracker URLs in JSON + misc fixes (#4855) * feat: issue tracker URLs in JSON + misc fixes * misc changes * feat: status update support for issues * feat: report metadata generation hook support * feat: added CLI summary of tickets created * misc changes * introduce `disable-unsigned-templates` flag (#4820) * introduce `disable-unsigned-templates` flag * minor * skip instead of exit * remove duplicate imports * use stats package + misc enhancements * force display warning + adjust skipped stats in unsigned count * include unsigned skipped templates without -dut flag --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> * Purge cache on global callback set (#4840) * purge cache on global callback set * lint * purging cache * purge cache in runner after loading templates * include internal cache from parsers + add global cache register/purge via config * remove disable cache purge option --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> * misc update * add application/octet-stream support * openapi: support path specific params * misc option + readme update --------- Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io> Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> Co-authored-by: Tarun Koyalwar <45962551+tarunKoyalwar@users.noreply.github.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: Mzack9999 <mzack9999@protonmail.com>
2024-03-14 03:08:53 +05:30
matcherName := GetMatcherName(matcher, matcherIndex)
result.Matches[matcherName] = matched
} else { // if it's a "named" matcher with OR condition, then display it
if matcherCondition == matchers.ORCondition && matcher.Name != "" {
result.Matches[matcher.Name] = matched
}
}
2021-03-09 16:35:53 +05:30
matches = true
} else if matcherCondition == matchers.ANDCondition {
if len(result.DynamicValues) > 0 {
return result, true
}
return result, false
2021-03-09 16:35:53 +05:30
}
}
result.Matched = matches
result.Extracted = len(result.OutputExtracts) > 0
if len(result.DynamicValues) > 0 && allInternalExtractors {
// only return early if all extractors are internal
// if some are internal and some are not then followthrough
return result, true
}
2021-09-16 20:35:43 +03:00
// Don't print if we have matchers, and they have not matched, regardless of extractor
if len(operators.Matchers) > 0 && !matches {
// if dynamic values are present then it is not a failure
if len(result.DynamicValues) > 0 {
return result, true
}
2020-12-30 13:26:55 +05:30
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.
if len(result.Extracts) > 0 || len(result.OutputExtracts) > 0 || matches {
2020-12-24 12:56:28 +05:30
return result, true
}
// if dynamic values are present then it is not a failure
if len(result.DynamicValues) > 0 {
return result, true
}
2020-12-24 12:56:28 +05:30
return nil, false
}
Fuzzing layer enhancements + input-types support (#4477) * feat: move fuzz package to root directory * feat: added support for input providers like openapi,postman,etc * feat: integration of new fuzzing logic in engine * bugfix: use and instead of or * fixed lint errors * go mod tidy * add new reqresp type + bump utils * custom http request parser * use new struct type RequestResponse * introduce unified input/target provider * abstract input formats via new inputprovider * completed input provider refactor * remove duplicated code * add sdk method to load targets * rename component url->path * add new yaml format + remove duplicated code * use gopkg.in/yaml.v3 for parsing * update .gitignore * refactor/move + docs fuzzing in http protocol * fuzz: header + query integration test using fuzzplayground * fix integration test runner in windows * feat add support for filter in http fuzz * rewrite header/query integration test with filter * add replace regex rule * support kv fuzzing + misc updates * add path fuzzing example + misc improvements * fix matchedURL + skip httpx on multi formats * cookie fuzz integration test * add json body + params body tests * feat add multipart/form-data fuzzing support * add all fuzz body integration test * misc bug fixes + minor refactor * add multipart form + body form unit tests * only run fuzzing templates if -fuzz flag is given * refactor/move fuzz playground server to pkg * fix integration test + refactor * add auth types and strategies * add file auth provider * start implementing auth logic in http * add logic in http protocol * static auth implemented for http * default :80,:443 normalization * feat: dynamic auth init * feat: dynamic auth using templates * validate targets count in openapi+swagger * inputformats: add support to accept variables * fix workflow integration test * update lazy cred fetch logic * fix unit test * drop postman support * domain related normalization * update secrets.yaml file format + misc updates * add auth prefetch option * remove old secret files * add fuzzing+auth related sdk options * fix/support multiple mode in kv header fuzzing * rename 'headers' -> 'header' in fuzzing rules * fix deadlock due to merge conflict resolution * misc update * add bool type in parsed value * add openapi validation+override+ new flags * misc updates * remove optional path parameters when unavailable * fix swagger.yaml file * misc updates * update print msg * multiple openapi validation enchancements + appMode * add optional params in required_openapi_vars.yaml file * improve warning/verbose msgs in format * fix skip-format-validation not working * use 'params/parameter' instead of 'variable' in openapi * add retry support for falky tests * fix nuclei loading ignored templates (#4849) * fix tag include logic * fix unit test * remove quoting in extractor output * remove quote in debug code command * feat: issue tracker URLs in JSON + misc fixes (#4855) * feat: issue tracker URLs in JSON + misc fixes * misc changes * feat: status update support for issues * feat: report metadata generation hook support * feat: added CLI summary of tickets created * misc changes * introduce `disable-unsigned-templates` flag (#4820) * introduce `disable-unsigned-templates` flag * minor * skip instead of exit * remove duplicate imports * use stats package + misc enhancements * force display warning + adjust skipped stats in unsigned count * include unsigned skipped templates without -dut flag --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> * Purge cache on global callback set (#4840) * purge cache on global callback set * lint * purging cache * purge cache in runner after loading templates * include internal cache from parsers + add global cache register/purge via config * remove disable cache purge option --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> * misc update * add application/octet-stream support * openapi: support path specific params * misc option + readme update --------- Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io> Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> Co-authored-by: Tarun Koyalwar <45962551+tarunKoyalwar@users.noreply.github.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: Mzack9999 <mzack9999@protonmail.com>
2024-03-14 03:08:53 +05:30
// GetMatcherName returns matchername of given matcher
func GetMatcherName(matcher *matchers.Matcher, matcherIndex int) string {
if matcher.Name != "" {
return matcher.Name
} else {
return matcher.Type.String() + "-" + strconv.Itoa(matcherIndex+1) // making the index start from 1 to be more readable
}
}
// ExecuteInternalExtractors executes internal dynamic extractors
func (operators *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 operators.Extractors {
if !extractor.Internal {
continue
}
for match := range extract(data, extractor) {
if _, ok := dynamicValues[extractor.Name]; !ok {
dynamicValues[extractor.Name] = match
}
}
}
return dynamicValues
}
// IsEmpty determines if the operator has matchers or extractors
func (operators *Operators) IsEmpty() bool {
return operators.Len() == 0
}
// Len calculates the sum of the number of matchers and extractors
func (operators *Operators) Len() int {
return len(operators.Matchers) + len(operators.Extractors)
}
// getExtractedValue takes array of extracted values if it only has one value
// then it is flattened and returned as a string else original type is returned
func getExtractedValue(values []string) any {
if len(values) == 1 {
return values[0]
} else {
return values
}
}
Fuzzing layer enhancements + input-types support (#4477) * feat: move fuzz package to root directory * feat: added support for input providers like openapi,postman,etc * feat: integration of new fuzzing logic in engine * bugfix: use and instead of or * fixed lint errors * go mod tidy * add new reqresp type + bump utils * custom http request parser * use new struct type RequestResponse * introduce unified input/target provider * abstract input formats via new inputprovider * completed input provider refactor * remove duplicated code * add sdk method to load targets * rename component url->path * add new yaml format + remove duplicated code * use gopkg.in/yaml.v3 for parsing * update .gitignore * refactor/move + docs fuzzing in http protocol * fuzz: header + query integration test using fuzzplayground * fix integration test runner in windows * feat add support for filter in http fuzz * rewrite header/query integration test with filter * add replace regex rule * support kv fuzzing + misc updates * add path fuzzing example + misc improvements * fix matchedURL + skip httpx on multi formats * cookie fuzz integration test * add json body + params body tests * feat add multipart/form-data fuzzing support * add all fuzz body integration test * misc bug fixes + minor refactor * add multipart form + body form unit tests * only run fuzzing templates if -fuzz flag is given * refactor/move fuzz playground server to pkg * fix integration test + refactor * add auth types and strategies * add file auth provider * start implementing auth logic in http * add logic in http protocol * static auth implemented for http * default :80,:443 normalization * feat: dynamic auth init * feat: dynamic auth using templates * validate targets count in openapi+swagger * inputformats: add support to accept variables * fix workflow integration test * update lazy cred fetch logic * fix unit test * drop postman support * domain related normalization * update secrets.yaml file format + misc updates * add auth prefetch option * remove old secret files * add fuzzing+auth related sdk options * fix/support multiple mode in kv header fuzzing * rename 'headers' -> 'header' in fuzzing rules * fix deadlock due to merge conflict resolution * misc update * add bool type in parsed value * add openapi validation+override+ new flags * misc updates * remove optional path parameters when unavailable * fix swagger.yaml file * misc updates * update print msg * multiple openapi validation enchancements + appMode * add optional params in required_openapi_vars.yaml file * improve warning/verbose msgs in format * fix skip-format-validation not working * use 'params/parameter' instead of 'variable' in openapi * add retry support for falky tests * fix nuclei loading ignored templates (#4849) * fix tag include logic * fix unit test * remove quoting in extractor output * remove quote in debug code command * feat: issue tracker URLs in JSON + misc fixes (#4855) * feat: issue tracker URLs in JSON + misc fixes * misc changes * feat: status update support for issues * feat: report metadata generation hook support * feat: added CLI summary of tickets created * misc changes * introduce `disable-unsigned-templates` flag (#4820) * introduce `disable-unsigned-templates` flag * minor * skip instead of exit * remove duplicate imports * use stats package + misc enhancements * force display warning + adjust skipped stats in unsigned count * include unsigned skipped templates without -dut flag --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> * Purge cache on global callback set (#4840) * purge cache on global callback set * lint * purging cache * purge cache in runner after loading templates * include internal cache from parsers + add global cache register/purge via config * remove disable cache purge option --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> * misc update * add application/octet-stream support * openapi: support path specific params * misc option + readme update --------- Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io> Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io> Co-authored-by: Tarun Koyalwar <45962551+tarunKoyalwar@users.noreply.github.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: Mzack9999 <mzack9999@protonmail.com>
2024-03-14 03:08:53 +05:30
// EvalBoolSlice evaluates a slice of bools using a logical AND
func EvalBoolSlice(slice []bool, isAnd bool) bool {
if len(slice) == 0 {
return false
}
result := slice[0]
for _, b := range slice[1:] {
if isAnd {
result = result && b
} else {
result = result || b
}
}
return result
}