From 4eeaf537284f9704fc274f1376f0be5195bbcdfd Mon Sep 17 00:00:00 2001 From: quobix Date: Thu, 24 Oct 2024 12:48:14 -0400 Subject: [PATCH] Fixed issue with JS plugins running async. Also bumped version of libopenapi to take advantage of the new reduced memory use. --- go.mod | 4 ++-- go.sum | 3 +++ plugin/javascript/js_plugin.go | 40 +++++++++++++++++++++++----------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index a783e810..1e9ca43b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.23.0 require ( github.com/alecthomas/chroma v0.10.0 - github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3 + github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc github.com/dustin/go-humanize v1.0.1 github.com/fsnotify/fsnotify v1.7.0 @@ -13,7 +13,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/mitchellh/mapstructure v1.5.0 github.com/pb33f/doctor v0.0.14 - github.com/pb33f/libopenapi v0.18.4 + github.com/pb33f/libopenapi v0.18.5 github.com/pb33f/libopenapi-validator v0.2.0 github.com/pterm/pterm v0.12.79 github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 diff --git a/go.sum b/go.sum index b79139fe..c53e7a9c 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yA github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3 h1:MXsAuToxwsTn5BEEYm2DheqIiC4jWGmkEJ1uy+KFhvQ= github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= +github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd h1:QMSNEh9uQkDjyPwu/J541GgSH+4hw+0skJDIj9HJ3mE= +github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc h1:MKYt39yZJi0Z9xEeRmDX2L4ocE0ETKcHKw6MVL3R+co= github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc/go.mod h1:VULptt4Q/fNzQUJlqY/GP3qHyU7ZH46mFkBZe0ZTokU= github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= @@ -162,6 +164,7 @@ github.com/pb33f/doctor v0.0.14 h1:Yei7f6Ksuc9RBjN3umiCEI8zN7ti3s5aveTT3sLi8Ts= github.com/pb33f/doctor v0.0.14/go.mod h1:1afDRJqurrftgMfRDbf1jWPoOyvdAsaK8+GHD8MiTLI= github.com/pb33f/libopenapi v0.18.4 h1:b39Spb8CY0jzormHkB2+U/UtaKegicHHJRY7c9PlTDQ= github.com/pb33f/libopenapi v0.18.4/go.mod h1:9ap4lXBHgxGyFwxtOfa+B1C3IQ0rvnqteqjJvJ11oiQ= +github.com/pb33f/libopenapi v0.18.5/go.mod h1:9ap4lXBHgxGyFwxtOfa+B1C3IQ0rvnqteqjJvJ11oiQ= github.com/pb33f/libopenapi-validator v0.2.0 h1:bixMgyHXRenN60L8927GtsWM8AE/p3fGvQdn6ZkgNHM= github.com/pb33f/libopenapi-validator v0.2.0/go.mod h1:DpluvEfTfDwfqTN2sgvTH14dPbzKDtE/vYQgcx3iNMs= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= diff --git a/plugin/javascript/js_plugin.go b/plugin/javascript/js_plugin.go index c1209e1c..042e500e 100644 --- a/plugin/javascript/js_plugin.go +++ b/plugin/javascript/js_plugin.go @@ -1,4 +1,4 @@ -// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley +// Copyright 2023-2004 Princess B33f Heavy Industries / Dave Shanley // SPDX-License-Identifier: MIT package javascript @@ -12,6 +12,7 @@ import ( "github.com/dop251/goja_nodejs/require" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v3" + "sync" ) type CoreFunction func(input any, context model.RuleFunctionContext) []model.RuleFunctionResult @@ -31,14 +32,11 @@ type JSRuleFunction struct { scriptParsed bool runtime *goja.Runtime coreFunctions map[string]interface{} + l sync.Mutex } func NewJSRuleFunction(ruleName, script string) JSEnabledRuleFunction { - rt := goja.New() - rt.SetFieldNameMapper(goja.TagFieldNameMapper("json", true)) - reg := new(require.Registry) - reg.Enable(rt) - console.Enable(rt) + rt := BuildVM() return &JSRuleFunction{ ruleName: ruleName, @@ -113,14 +111,19 @@ func (j *JSRuleFunction) CheckScript() error { func (j *JSRuleFunction) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) []model.RuleFunctionResult { + // rule need their own runtime because these functions run async, the same runtime will become polluted. + rt := BuildVM() + var results []model.RuleFunctionResult + var runtimeErr error for _, node := range nodes { var enc interface{} _ = node.Decode(&enc) - - runtimeErr := j.runtime.Set("context", context) + j.l.Lock() + runtimeErr = rt.Set("context", context) // prevent any run time issues. + j.l.Unlock() if runtimeErr != nil { return []model.RuleFunctionResult{ { @@ -134,9 +137,11 @@ func (j *JSRuleFunction) RunRule(nodes []*yaml.Node, context model.RuleFunctionC } } + _, runtimeErr = rt.RunString(j.script) + // register core functions for name, function := range j.coreFunctions { - coreErr := j.runtime.Set(fmt.Sprintf("vacuum_%s", name), function) + coreErr := rt.Set(fmt.Sprintf("vacuum_%s", name), function) if coreErr != nil { return []model.RuleFunctionResult{ { @@ -151,12 +156,12 @@ func (j *JSRuleFunction) RunRule(nodes []*yaml.Node, context model.RuleFunctionC } } - runRule, ok := goja.AssertFunction(j.runtime.Get("runRule")) + runRule, ok := goja.AssertFunction(rt.Get("runRule")) if !ok { return []model.RuleFunctionResult{ { - Message: fmt.Sprintf("'runRule' is not defined as a JavaScript function: '%s': %s ", - j.ruleName, runtimeErr.Error()), + Message: fmt.Sprintf("'runRule' is not defined as a JavaScript function: '%s'", + j.ruleName), StartNode: node, EndNode: node, Path: fmt.Sprint(context.Given), @@ -167,7 +172,7 @@ func (j *JSRuleFunction) RunRule(nodes []*yaml.Node, context model.RuleFunctionC var functionResults []model.RuleFunctionResult // run JS rule! - runtimeValue := j.runtime.ToValue(enc) + runtimeValue := rt.ToValue(enc) ruleOutput, rErr := runRule(goja.Undefined(), runtimeValue) if rErr != nil { if jserr, okE := rErr.(*goja.Exception); okE { @@ -219,3 +224,12 @@ func (j *JSRuleFunction) RunRule(nodes []*yaml.Node, context model.RuleFunctionC } return results } + +func BuildVM() *goja.Runtime { + rt := goja.New() + rt.SetFieldNameMapper(goja.TagFieldNameMapper("json", true)) + reg := new(require.Registry) + reg.Enable(rt) + console.Enable(rt) + return rt +}