From 64cb4bd1f34efc4ea751765682e37b59c916b1e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 05:03:46 +0000 Subject: [PATCH 1/7] chore(deps): bump golang from 1.15-alpine to 1.16.6-alpine Bumps golang from 1.15-alpine to 1.16.6-alpine. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6dda403a..d915a432 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.15-alpine as build-env +FROM golang:1.16.6-alpine as build-env RUN GO111MODULE=on go get -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei FROM alpine:latest From d29d7f8b56ee8e66e41f2453c2c83812736e7d08 Mon Sep 17 00:00:00 2001 From: mzack Date: Mon, 19 Jul 2021 23:34:22 +0200 Subject: [PATCH 2/7] Fixing missing headers in request dump --- v2/pkg/protocols/http/request.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/v2/pkg/protocols/http/request.go b/v2/pkg/protocols/http/request.go index c6028dca..4c7c873f 100644 --- a/v2/pkg/protocols/http/request.go +++ b/v2/pkg/protocols/http/request.go @@ -263,19 +263,6 @@ func (r *Request) executeRequest(reqURL string, request *generatedRequest, previ err error ) - // For race conditions we can't dump the request body at this point as it's already waiting the open-gate event, already handled with a similar code within the race function - if !request.original.Race { - dumpedRequest, err = dump(request, reqURL) - if err != nil { - return err - } - - if r.options.Options.Debug || r.options.Options.DebugRequests { - gologger.Info().Msgf("[%s] Dumped HTTP request for %s\n\n", r.options.TemplateID, reqURL) - gologger.Print().Msgf("%s", string(dumpedRequest)) - } - } - var formedURL string var hostname string timeStart := time.Now() @@ -314,6 +301,20 @@ func (r *Request) executeRequest(reqURL string, request *generatedRequest, previ resp, err = r.httpClient.Do(request.request) } } + + // For race conditions we can't dump the request body at this point as it's already waiting the open-gate event, already handled with a similar code within the race function + if !request.original.Race { + dumpedRequest, err = dump(request, reqURL) + if err != nil { + return err + } + + if r.options.Options.Debug || r.options.Options.DebugRequests { + gologger.Info().Msgf("[%s] Dumped HTTP request for %s\n\n", r.options.TemplateID, reqURL) + gologger.Print().Msgf("%s", string(dumpedRequest)) + } + } + if resp == nil { err = errors.New("no response got for request") } From 2bea3f4b14287a6425e561ef98ed9a142f28a1b2 Mon Sep 17 00:00:00 2001 From: TheSecEng Date: Tue, 20 Jul 2021 22:32:44 -0700 Subject: [PATCH 3/7] all for custom preprocessor --- v2/pkg/catalog/loader/loader.go | 15 +++++++++++---- v2/pkg/templates/compile.go | 8 ++++++-- v2/pkg/templates/preprocessors.go | 4 ++++ v2/pkg/templates/templates.go | 2 ++ v2/pkg/templates/workflows.go | 16 ++++++++-------- 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/v2/pkg/catalog/loader/loader.go b/v2/pkg/catalog/loader/loader.go index a7117575..37b75943 100644 --- a/v2/pkg/catalog/loader/loader.go +++ b/v2/pkg/catalog/loader/loader.go @@ -38,6 +38,8 @@ type Store struct { templates []*templates.Template workflows []*templates.Template + + preprocessor templates.Preprocessor } // New creates a new template store based on provided configuration @@ -76,6 +78,11 @@ func (s *Store) Workflows() []*templates.Template { return s.workflows } +// RegisterPreprocessor allows a custom preprocessor to be passed to the store to run against templates +func (s *Store) RegisterPreprocessor(preprocessor templates.Preprocessor) { + s.preprocessor = preprocessor +} + // Load loads all the templates from a store, performs filtering and returns // the complete compiled templates for a nuclei execution configuration. func (s *Store) Load() { @@ -105,7 +112,7 @@ func (s *Store) ValidateTemplates(templatesList, workflowsList []string) bool { gologger.Error().Msgf("Error occurred loading template %s: %s\n", k, err) continue } - _, err = templates.Parse(k, s.config.ExecutorOptions) + _, err = templates.Parse(k, s.preprocessor, s.config.ExecutorOptions) if err != nil { if strings.Contains(err.Error(), "cannot create template executer") { continue @@ -129,7 +136,7 @@ func (s *Store) ValidateTemplates(templatesList, workflowsList []string) bool { notErrored = false gologger.Error().Msgf("Error occurred loading workflow %s: %s\n", k, err) } - _, err = templates.Parse(k, s.config.ExecutorOptions) + _, err = templates.Parse(k, s.preprocessor, s.config.ExecutorOptions) if err != nil { if strings.Contains(err.Error(), "cannot create template executer") { continue @@ -156,7 +163,7 @@ func (s *Store) LoadTemplates(templatesList []string) []*templates.Template { gologger.Warning().Msgf("Could not load template %s: %s\n", k, err) } if loaded { - parsed, err := templates.Parse(k, s.config.ExecutorOptions) + parsed, err := templates.Parse(k, s.preprocessor, s.config.ExecutorOptions) if err != nil { gologger.Warning().Msgf("Could not parse template %s: %s\n", k, err) } else if parsed != nil { @@ -179,7 +186,7 @@ func (s *Store) LoadWorkflows(workflowsList []string) []*templates.Template { gologger.Warning().Msgf("Could not load workflow %s: %s\n", k, err) } if loaded { - parsed, err := templates.Parse(k, s.config.ExecutorOptions) + parsed, err := templates.Parse(k, s.preprocessor, s.config.ExecutorOptions) if err != nil { gologger.Warning().Msgf("Could not parse workflow %s: %s\n", k, err) } else if parsed != nil { diff --git a/v2/pkg/templates/compile.go b/v2/pkg/templates/compile.go index f735db5c..7ec032c8 100644 --- a/v2/pkg/templates/compile.go +++ b/v2/pkg/templates/compile.go @@ -18,7 +18,7 @@ import ( // Parse parses a yaml request template file //nolint:gocritic // this cannot be passed by pointer -func Parse(filePath string, options protocols.ExecuterOptions) (*Template, error) { +func Parse(filePath string, preprocessor Preprocessor, options protocols.ExecuterOptions) (*Template, error) { template := &Template{} f, err := os.Open(filePath) @@ -33,6 +33,10 @@ func Parse(filePath string, options protocols.ExecuterOptions) (*Template, error } data = template.expandPreprocessors(data) + if preprocessor != nil { + data = preprocessor.Process(data) + } + err = yaml.NewDecoder(bytes.NewReader(data)).Decode(template) if err != nil { return nil, err @@ -63,7 +67,7 @@ func Parse(filePath string, options protocols.ExecuterOptions) (*Template, error if err != nil { return nil, errors.Wrap(err, "could not create workflow loader") } - compileWorkflow(&options, compiled, loader) + compileWorkflow(preprocessor, &options, compiled, loader) template.CompiledWorkflow = compiled template.CompiledWorkflow.Options = &options } diff --git a/v2/pkg/templates/preprocessors.go b/v2/pkg/templates/preprocessors.go index 83241a0a..ef6edeb1 100644 --- a/v2/pkg/templates/preprocessors.go +++ b/v2/pkg/templates/preprocessors.go @@ -8,6 +8,10 @@ import ( "github.com/segmentio/ksuid" ) +type Preprocessor interface { + Process(data []byte) []byte +} + var preprocessorRegex = regexp.MustCompile(`\{\{([a-z0-9_]+)\}\}`) // expandPreprocessors expands the pre-processors if any for a template data. diff --git a/v2/pkg/templates/templates.go b/v2/pkg/templates/templates.go index 12c135e4..ef0e4f59 100644 --- a/v2/pkg/templates/templates.go +++ b/v2/pkg/templates/templates.go @@ -37,4 +37,6 @@ type Template struct { Executer protocols.Executer `yaml:"-" json:"-"` Path string `yaml:"-" json:"-"` + + preprocessor Preprocessor } diff --git a/v2/pkg/templates/workflows.go b/v2/pkg/templates/workflows.go index 96f71c5a..628365d0 100644 --- a/v2/pkg/templates/workflows.go +++ b/v2/pkg/templates/workflows.go @@ -8,9 +8,9 @@ import ( ) // compileWorkflow compiles the workflow for execution -func compileWorkflow(options *protocols.ExecuterOptions, workflow *workflows.Workflow, loader compile.WorkflowLoader) { +func compileWorkflow(preprocessor Preprocessor, options *protocols.ExecuterOptions, workflow *workflows.Workflow, loader compile.WorkflowLoader) { for _, workflow := range workflow.Workflows { - if err := parseWorkflow(workflow, options, loader); err != nil { + if err := parseWorkflow(preprocessor, workflow, options, loader); err != nil { gologger.Warning().Msgf("Could not parse workflow: %v\n", err) continue } @@ -18,24 +18,24 @@ func compileWorkflow(options *protocols.ExecuterOptions, workflow *workflows.Wor } // parseWorkflow parses and compiles all templates in a workflow recursively -func parseWorkflow(workflow *workflows.WorkflowTemplate, options *protocols.ExecuterOptions, loader compile.WorkflowLoader) error { +func parseWorkflow(preprocessor Preprocessor, workflow *workflows.WorkflowTemplate, options *protocols.ExecuterOptions, loader compile.WorkflowLoader) error { shouldNotValidate := false if len(workflow.Subtemplates) > 0 || len(workflow.Matchers) > 0 { shouldNotValidate = true } - if err := parseWorkflowTemplate(workflow, options, loader, shouldNotValidate); err != nil { + if err := parseWorkflowTemplate(workflow, preprocessor, options, loader, shouldNotValidate); err != nil { return err } for _, subtemplates := range workflow.Subtemplates { - if err := parseWorkflow(subtemplates, options, loader); err != nil { + if err := parseWorkflow(preprocessor, subtemplates, options, loader); err != nil { gologger.Warning().Msgf("Could not parse workflow: %v\n", err) continue } } for _, matcher := range workflow.Matchers { for _, subtemplates := range matcher.Subtemplates { - if err := parseWorkflow(subtemplates, options, loader); err != nil { + if err := parseWorkflow(preprocessor, subtemplates, options, loader); err != nil { gologger.Warning().Msgf("Could not parse workflow: %v\n", err) continue } @@ -45,7 +45,7 @@ func parseWorkflow(workflow *workflows.WorkflowTemplate, options *protocols.Exec } // parseWorkflowTemplate parses a workflow template creating an executer -func parseWorkflowTemplate(workflow *workflows.WorkflowTemplate, options *protocols.ExecuterOptions, loader compile.WorkflowLoader, noValidate bool) error { +func parseWorkflowTemplate(workflow *workflows.WorkflowTemplate, preprocessor Preprocessor, options *protocols.ExecuterOptions, loader compile.WorkflowLoader, noValidate bool) error { var paths []string if len(workflow.Tags) > 0 { @@ -68,7 +68,7 @@ func parseWorkflowTemplate(workflow *workflows.WorkflowTemplate, options *protoc Interactsh: options.Interactsh, ProjectFile: options.ProjectFile, } - template, err := Parse(path, opts) + template, err := Parse(path, preprocessor, opts) if err != nil { gologger.Warning().Msgf("Could not parse workflow template %s: %v\n", path, err) continue From 40f2c562dc29fa81a94a58dee5d6723e2b372041 Mon Sep 17 00:00:00 2001 From: TheSecEng Date: Tue, 20 Jul 2021 23:27:12 -0700 Subject: [PATCH 4/7] implemente custom helper logic --- v2/pkg/operators/common/dsl/dsl.go | 184 +++++++++++++---------------- 1 file changed, 79 insertions(+), 105 deletions(-) diff --git a/v2/pkg/operators/common/dsl/dsl.go b/v2/pkg/operators/common/dsl/dsl.go index 65920539..ea8a97b9 100644 --- a/v2/pkg/operators/common/dsl/dsl.go +++ b/v2/pkg/operators/common/dsl/dsl.go @@ -29,113 +29,88 @@ const ( withMaxRandArgsSize = withCutSetArgsSize ) -// HelperFunctions contains the dsl helper functions -func HelperFunctions() map[string]govaluate.ExpressionFunction { - functions := make(map[string]govaluate.ExpressionFunction) - - functions["len"] = func(args ...interface{}) (interface{}, error) { +var functions = map[string]govaluate.ExpressionFunction{ + "len": func(args ...interface{}) (interface{}, error) { length := len(types.ToString(args[0])) return float64(length), nil - } - - functions["toupper"] = func(args ...interface{}) (interface{}, error) { + }, + "toupper": func(args ...interface{}) (interface{}, error) { return strings.ToUpper(types.ToString(args[0])), nil - } - - functions["tolower"] = func(args ...interface{}) (interface{}, error) { + }, + "tolower": func(args ...interface{}) (interface{}, error) { return strings.ToLower(types.ToString(args[0])), nil - } - - functions["replace"] = func(args ...interface{}) (interface{}, error) { + }, + "replace": func(args ...interface{}) (interface{}, error) { return strings.ReplaceAll(types.ToString(args[0]), types.ToString(args[1]), types.ToString(args[2])), nil - } - - functions["replace_regex"] = func(args ...interface{}) (interface{}, error) { + }, + "replace_regex": func(args ...interface{}) (interface{}, error) { compiled, err := regexp.Compile(types.ToString(args[1])) if err != nil { return nil, err } return compiled.ReplaceAllString(types.ToString(args[0]), types.ToString(args[2])), nil - } - - functions["trim"] = func(args ...interface{}) (interface{}, error) { + }, + "trim": func(args ...interface{}) (interface{}, error) { return strings.Trim(types.ToString(args[0]), types.ToString(args[2])), nil - } - - functions["trimleft"] = func(args ...interface{}) (interface{}, error) { + }, + "trimleft": func(args ...interface{}) (interface{}, error) { return strings.TrimLeft(types.ToString(args[0]), types.ToString(args[1])), nil - } - - functions["trimright"] = func(args ...interface{}) (interface{}, error) { + }, + "trimright": func(args ...interface{}) (interface{}, error) { return strings.TrimRight(types.ToString(args[0]), types.ToString(args[1])), nil - } - - functions["trimspace"] = func(args ...interface{}) (interface{}, error) { + }, + "trimspace": func(args ...interface{}) (interface{}, error) { return strings.TrimSpace(types.ToString(args[0])), nil - } - - functions["trimprefix"] = func(args ...interface{}) (interface{}, error) { + }, + "trimprefix": func(args ...interface{}) (interface{}, error) { return strings.TrimPrefix(types.ToString(args[0]), types.ToString(args[1])), nil - } - - functions["trimsuffix"] = func(args ...interface{}) (interface{}, error) { + }, + "trimsuffix": func(args ...interface{}) (interface{}, error) { return strings.TrimSuffix(types.ToString(args[0]), types.ToString(args[1])), nil - } - - functions["reverse"] = func(args ...interface{}) (interface{}, error) { + }, + "reverse": func(args ...interface{}) (interface{}, error) { return reverseString(types.ToString(args[0])), nil - } - + }, // encoding - functions["base64"] = func(args ...interface{}) (interface{}, error) { + "base64": func(args ...interface{}) (interface{}, error) { sEnc := base64.StdEncoding.EncodeToString([]byte(types.ToString(args[0]))) return sEnc, nil - } - + }, // python encodes to base64 with lines of 76 bytes terminated by new line "\n" - functions["base64_py"] = func(args ...interface{}) (interface{}, error) { + "base64_py": func(args ...interface{}) (interface{}, error) { sEnc := base64.StdEncoding.EncodeToString([]byte(types.ToString(args[0]))) return insertInto(sEnc, 76, '\n'), nil - } - - functions["base64_decode"] = func(args ...interface{}) (interface{}, error) { + }, + "base64_decode": func(args ...interface{}) (interface{}, error) { return base64.StdEncoding.DecodeString(types.ToString(args[0])) - } - - functions["url_encode"] = func(args ...interface{}) (interface{}, error) { + }, + "url_encode": func(args ...interface{}) (interface{}, error) { return url.PathEscape(types.ToString(args[0])), nil - } - - functions["url_decode"] = func(args ...interface{}) (interface{}, error) { + }, + "url_decode": func(args ...interface{}) (interface{}, error) { return url.PathUnescape(types.ToString(args[0])) - } - - functions["hex_encode"] = func(args ...interface{}) (interface{}, error) { + }, + "hex_encode": func(args ...interface{}) (interface{}, error) { return hex.EncodeToString([]byte(types.ToString(args[0]))), nil - } - - functions["hex_decode"] = func(args ...interface{}) (interface{}, error) { + }, + "hex_decode": func(args ...interface{}) (interface{}, error) { hx, _ := hex.DecodeString(types.ToString(args[0])) return string(hx), nil - } - - functions["html_escape"] = func(args ...interface{}) (interface{}, error) { + }, + "html_escape": func(args ...interface{}) (interface{}, error) { return html.EscapeString(types.ToString(args[0])), nil - } - - functions["html_unescape"] = func(args ...interface{}) (interface{}, error) { + }, + "html_unescape": func(args ...interface{}) (interface{}, error) { return html.UnescapeString(types.ToString(args[0])), nil - } - + }, // hashing - functions["md5"] = func(args ...interface{}) (interface{}, error) { + "md5": func(args ...interface{}) (interface{}, error) { hash := md5.Sum([]byte(types.ToString(args[0]))) return hex.EncodeToString(hash[:]), nil - } - - functions["sha256"] = func(args ...interface{}) (interface{}, error) { + }, + "sha256": func(args ...interface{}) (interface{}, error) { h := sha256.New() _, err := h.Write([]byte(types.ToString(args[0]))) @@ -143,9 +118,8 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { return nil, err } return hex.EncodeToString(h.Sum(nil)), nil - } - - functions["sha1"] = func(args ...interface{}) (interface{}, error) { + }, + "sha1": func(args ...interface{}) (interface{}, error) { h := sha1.New() _, err := h.Write([]byte(types.ToString(args[0]))) @@ -153,27 +127,23 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { return nil, err } return hex.EncodeToString(h.Sum(nil)), nil - } - - functions["mmh3"] = func(args ...interface{}) (interface{}, error) { + }, + "mmh3": func(args ...interface{}) (interface{}, error) { return fmt.Sprintf("%d", int32(murmur3.Sum32WithSeed([]byte(types.ToString(args[0])), 0))), nil - } - + }, // search - functions["contains"] = func(args ...interface{}) (interface{}, error) { + "contains": func(args ...interface{}) (interface{}, error) { return strings.Contains(types.ToString(args[0]), types.ToString(args[1])), nil - } - - functions["regex"] = func(args ...interface{}) (interface{}, error) { + }, + "regex": func(args ...interface{}) (interface{}, error) { compiled, err := regexp.Compile(types.ToString(args[0])) if err != nil { return nil, err } return compiled.MatchString(types.ToString(args[1])), nil - } - + }, // random generators - functions["rand_char"] = func(args ...interface{}) (interface{}, error) { + "rand_char": func(args ...interface{}) (interface{}, error) { chars := letters + numbers bad := "" if len(args) >= 1 { @@ -184,9 +154,8 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { } chars = trimAll(chars, bad) return chars[rand.Intn(len(chars))], nil - } - - functions["rand_base"] = func(args ...interface{}) (interface{}, error) { + }, + "rand_base": func(args ...interface{}) (interface{}, error) { l := 0 bad := "" base := letters + numbers @@ -202,9 +171,8 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { } base = trimAll(base, bad) return randSeq(base, l), nil - } - - functions["rand_text_alphanumeric"] = func(args ...interface{}) (interface{}, error) { + }, + "rand_text_alphanumeric": func(args ...interface{}) (interface{}, error) { l := 0 bad := "" chars := letters + numbers @@ -217,9 +185,8 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { } chars = trimAll(chars, bad) return randSeq(chars, l), nil - } - - functions["rand_text_alpha"] = func(args ...interface{}) (interface{}, error) { + }, + "rand_text_alpha": func(args ...interface{}) (interface{}, error) { l := 0 bad := "" chars := letters @@ -232,9 +199,8 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { } chars = trimAll(chars, bad) return randSeq(chars, l), nil - } - - functions["rand_text_numeric"] = func(args ...interface{}) (interface{}, error) { + }, + "rand_text_numeric": func(args ...interface{}) (interface{}, error) { l := 0 bad := "" chars := numbers @@ -247,9 +213,8 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { } chars = trimAll(chars, bad) return randSeq(chars, l), nil - } - - functions["rand_int"] = func(args ...interface{}) (interface{}, error) { + }, + "rand_int": func(args ...interface{}) (interface{}, error) { min := 0 max := math.MaxInt32 @@ -260,17 +225,26 @@ func HelperFunctions() map[string]govaluate.ExpressionFunction { max = args[1].(int) } return rand.Intn(max-min) + min, nil - } - + }, // Time Functions - functions["waitfor"] = func(args ...interface{}) (interface{}, error) { + "waitfor": func(args ...interface{}) (interface{}, error) { seconds := args[0].(float64) time.Sleep(time.Duration(seconds) * time.Second) return true, nil - } + }, +} + +// HelperFunctions contains the dsl helper functions +func HelperFunctions() map[string]govaluate.ExpressionFunction { return functions } +func AddHelperFunction(key string, value func(args ...interface{}) (interface{}, error)) { + if _, ok := functions[key]; !ok { + functions[key] = value + } +} + func reverseString(s string) string { runes := []rune(s) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { From 9aa783459d248b5fe2b623c2e6f8a12e91cec842 Mon Sep 17 00:00:00 2001 From: cw Date: Wed, 21 Jul 2021 16:48:01 +0200 Subject: [PATCH 5/7] Adding DS (DNSSec) Tag to available DNS-Record type --- v2/pkg/protocols/dns/dns.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v2/pkg/protocols/dns/dns.go b/v2/pkg/protocols/dns/dns.go index 5fdf49cc..0538186f 100644 --- a/v2/pkg/protocols/dns/dns.go +++ b/v2/pkg/protocols/dns/dns.go @@ -118,6 +118,8 @@ func questionTypeToInt(questionType string) uint16 { question = dns.TypeMX case "TXT": question = dns.TypeTXT + case "DS": + question = dns.TypeDS case "AAAA": question = dns.TypeAAAA } From 7ebab7875bde8c99c091e85808bc9ee5a89fa317 Mon Sep 17 00:00:00 2001 From: Zachary Schulze Date: Fri, 23 Jul 2021 11:39:30 -0700 Subject: [PATCH 6/7] update - Added AddHelperFunction comment/usage - AddHelperFunction returns err on duplicate key definitions --- v2/pkg/operators/common/dsl/dsl.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/v2/pkg/operators/common/dsl/dsl.go b/v2/pkg/operators/common/dsl/dsl.go index ea8a97b9..37820b9a 100644 --- a/v2/pkg/operators/common/dsl/dsl.go +++ b/v2/pkg/operators/common/dsl/dsl.go @@ -7,6 +7,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/hex" + "errors" "fmt" "html" "math" @@ -234,15 +235,18 @@ var functions = map[string]govaluate.ExpressionFunction{ }, } -// HelperFunctions contains the dsl helper functions +// HelperFunctions returns the dsl helper functions func HelperFunctions() map[string]govaluate.ExpressionFunction { return functions } -func AddHelperFunction(key string, value func(args ...interface{}) (interface{}, error)) { +// AddHelperFunction allows creation of additiona helper functions to be supported with templates +func AddHelperFunction(key string, value func(args ...interface{}) (interface{}, error)) error { if _, ok := functions[key]; !ok { functions[key] = value + return nil } + return errors.New("duplicate helper function key defined") } func reverseString(s string) string { From 37626627ef9d8e95846a99115b03f5627a0f9feb Mon Sep 17 00:00:00 2001 From: Zachary Schulze Date: Fri, 23 Jul 2021 11:43:50 -0700 Subject: [PATCH 7/7] remove unused preprocessor from template --- v2/pkg/templates/templates.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/v2/pkg/templates/templates.go b/v2/pkg/templates/templates.go index ef0e4f59..12c135e4 100644 --- a/v2/pkg/templates/templates.go +++ b/v2/pkg/templates/templates.go @@ -37,6 +37,4 @@ type Template struct { Executer protocols.Executer `yaml:"-" json:"-"` Path string `yaml:"-" json:"-"` - - preprocessor Preprocessor }