From f3e78b7fde9f614c490f2784ea774505aa7e78fc Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Wed, 13 Nov 2024 15:15:00 +0530 Subject: [PATCH 01/19] feat: added support for http module in tengo --- plugins/internal/tengoutil/secure_script.go | 45 ++++++++++++++++++- .../internal/tengoutil/secure_script_test.go | 1 + 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 388fd2cb8..83b76c1b2 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -2,6 +2,9 @@ package tengoutil import ( "fmt" + "io" + + "net/http" "github.com/d5/tengo/v2" "github.com/d5/tengo/v2/stdlib" @@ -15,10 +18,12 @@ const ( func NewSecureScript(input []byte, globals map[string]interface{}) (*tengo.Script, error) { s := tengo.NewScript(input) - s.SetImports(stdlib.GetModuleMap( + modules := stdlib.GetModuleMap( // `os` is excluded, should *not* be importable from script. "math", "text", "times", "rand", "fmt", "json", "base64", "hex", "enum", - )) + ) + modules.AddBuiltinModule("http", createHTTPModule()) + s.SetImports(modules) s.SetMaxAllocs(maxAllocs) s.SetMaxConstObjects(maxConsts) @@ -30,3 +35,39 @@ func NewSecureScript(input []byte, globals map[string]interface{}) (*tengo.Scrip return s, nil } + +func createHTTPModule() map[string]tengo.Object { + return map[string]tengo.Object{ + "get": &tengo.UserFunction{ + Name: "get", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, fmt.Errorf("expected 1 argument, got %d", len(args)) + } + + url, ok := tengo.ToString(args[0]) + if !ok { + return nil, fmt.Errorf("expected argument 1 (URL) to be a string") + } + + resp, err := http.Get(url) + if err != nil { + return &tengo.Error{Value: &tengo.String{Value: err.Error()}}, nil + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return &tengo.Error{Value: &tengo.String{Value: err.Error()}}, nil + } + + return &tengo.Map{ + Value: map[string]tengo.Object{ + "body": &tengo.String{Value: string(body)}, + "code": &tengo.Int{Value: int64(resp.StatusCode)}, + }, + }, nil + }, + }, + } +} diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index cb2445111..8c45f8214 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -22,6 +22,7 @@ func TestNewSecureScript(t *testing.T) { base64 := import("base64") hex := import("hex") enum := import("enum") + http := import("http) `)), nil) assert.NoError(t, err) _, err = s.Compile() From d44a27c877aed92c9cb55e52bb4838c9f59fbae3 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Wed, 13 Nov 2024 15:39:46 +0530 Subject: [PATCH 02/19] unit test --- .../internal/tengoutil/secure_script_test.go | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index 8c45f8214..369faada2 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -22,7 +22,6 @@ func TestNewSecureScript(t *testing.T) { base64 := import("base64") hex := import("hex") enum := import("enum") - http := import("http) `)), nil) assert.NoError(t, err) _, err = s.Compile() @@ -56,4 +55,34 @@ func TestNewSecureScript(t *testing.T) { _, err = s.Compile() assert.NoError(t, err) }) + + t.Run("HTTP module test", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"message": "Hello, world!"}`)) + })) + defer ts.Close() + + script := heredoc.Docf(` + http := import("http") + json := import("json") + resp := http.get("%s") + data := json.decode(resp.body) + result := data.message + `, ts.URL) + + s, err := NewSecureScript([]byte(script), nil) + assert.NoError(t, err) + + compiledScript, err := s.Compile() + assert.NoError(t, err) + + err = compiledScript.Run() + assert.NoError(t, err) + + result, err := compiledScript.Get("result") + assert.NoError(t, err) + + assert.Equal(t, "Hello, world!", result.String()) + }) } From 15c907a0c222ffcdc8f492665178bca088ca1d27 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Wed, 13 Nov 2024 15:42:59 +0530 Subject: [PATCH 03/19] removed test --- .../internal/tengoutil/secure_script_test.go | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index 369faada2..cb2445111 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -55,34 +55,4 @@ func TestNewSecureScript(t *testing.T) { _, err = s.Compile() assert.NoError(t, err) }) - - t.Run("HTTP module test", func(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"message": "Hello, world!"}`)) - })) - defer ts.Close() - - script := heredoc.Docf(` - http := import("http") - json := import("json") - resp := http.get("%s") - data := json.decode(resp.body) - result := data.message - `, ts.URL) - - s, err := NewSecureScript([]byte(script), nil) - assert.NoError(t, err) - - compiledScript, err := s.Compile() - assert.NoError(t, err) - - err = compiledScript.Run() - assert.NoError(t, err) - - result, err := compiledScript.Get("result") - assert.NoError(t, err) - - assert.Equal(t, "Hello, world!", result.String()) - }) } From c75ce59e0ddec01e33e8b75eb9ce38ffecf13f88 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Wed, 13 Nov 2024 15:45:30 +0530 Subject: [PATCH 04/19] unit test check --- plugins/internal/tengoutil/secure_script_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index cb2445111..b4c50bd04 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -55,4 +55,15 @@ func TestNewSecureScript(t *testing.T) { _, err = s.Compile() assert.NoError(t, err) }) + + t.Run("Allows import of custom http module", func(t *testing.T) { + s, err := NewSecureScript(([]byte)(heredoc.Doc(` + http := import("http") + response := http.get("http://example.com") + response.body + `)), nil) + assert.NoError(t, err) + _, err = s.Compile() + assert.NoError(t, err) + }) } From 2dd93095961b0f9ead773d9682646861907caf27 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Wed, 13 Nov 2024 15:55:17 +0530 Subject: [PATCH 05/19] fixed lint --- plugins/internal/tengoutil/secure_script.go | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 83b76c1b2..7ca121dfa 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -3,7 +3,6 @@ package tengoutil import ( "fmt" "io" - "net/http" "github.com/d5/tengo/v2" From fd4d58263c533087bfe12b1f8f7fdf70ab7e4d85 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Wed, 13 Nov 2024 16:04:09 +0530 Subject: [PATCH 06/19] fixed lint --- plugins/internal/tengoutil/secure_script.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 7ca121dfa..df953564d 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -1,9 +1,11 @@ package tengoutil import ( + "context" "fmt" "io" "net/http" + "time" "github.com/d5/tengo/v2" "github.com/d5/tengo/v2/stdlib" @@ -49,15 +51,22 @@ func createHTTPModule() map[string]tengo.Object { return nil, fmt.Errorf("expected argument 1 (URL) to be a string") } - resp, err := http.Get(url) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, err + } + resp, err := http.DefaultClient.Do(req) if err != nil { - return &tengo.Error{Value: &tengo.String{Value: err.Error()}}, nil + return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return &tengo.Error{Value: &tengo.String{Value: err.Error()}}, nil + return nil, err } return &tengo.Map{ From d9d25175eba6b0256a19f09ce26003fe299b6457 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 09:18:38 +0530 Subject: [PATCH 07/19] generic with support for headers --- plugins/internal/tengoutil/secure_script.go | 25 +++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index df953564d..2025c6147 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -42,8 +42,8 @@ func createHTTPModule() map[string]tengo.Object { "get": &tengo.UserFunction{ Name: "get", Value: func(args ...tengo.Object) (tengo.Object, error) { - if len(args) != 1 { - return nil, fmt.Errorf("expected 1 argument, got %d", len(args)) + if len(args) < 1 || len(args) > 2 { + return nil, fmt.Errorf("expected 1 or 2 arguments, got %d", len(args)) } url, ok := tengo.ToString(args[0]) @@ -51,6 +51,21 @@ func createHTTPModule() map[string]tengo.Object { return nil, fmt.Errorf("expected argument 1 (URL) to be a string") } + headers := make(map[string]string) + if len(args) == 2 { + headerMap, ok := args[1].(*tengo.Map) + if !ok { + return nil, fmt.Errorf("expected argument 2 (headers) to be a map") + } + for key, value := range headerMap.Value { + strValue, valueOk := tengo.ToString(value) + if !valueOk { + return nil, fmt.Errorf("header values must be strings") + } + headers[key] = strValue + } + } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -58,6 +73,11 @@ func createHTTPModule() map[string]tengo.Object { if err != nil { return nil, err } + + for key, value := range headers { + req.Header.Add(key, value) + } + resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err @@ -79,3 +99,4 @@ func createHTTPModule() map[string]tengo.Object { }, } } + From 8a952f0266c2303d8aaf3895bfa568e10cab9346 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 09:22:23 +0530 Subject: [PATCH 08/19] fixed lint --- plugins/internal/tengoutil/secure_script.go | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 2025c6147..883fbfe69 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -99,4 +99,3 @@ func createHTTPModule() map[string]tengo.Object { }, } } - From ea0749620940582adeb2c54f6ef6b0e10ccb8cf2 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 13:40:52 +0530 Subject: [PATCH 09/19] made the HTTP userfunc modular - resolved comments --- plugins/internal/tengoutil/secure_script.go | 148 ++++++++++++-------- 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 883fbfe69..55d74221b 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -2,6 +2,7 @@ package tengoutil import ( "context" + "errors" "fmt" "io" "net/http" @@ -16,14 +17,24 @@ const ( maxConsts = 500 ) +const ( + expectedArgsLength = 2 + defaultTimeout = 5 * time.Second +) + +var HTTPModule = map[string]tengo.Object{ + "get": httpGetFunction, +} + func NewSecureScript(input []byte, globals map[string]interface{}) (*tengo.Script, error) { s := tengo.NewScript(input) + val := executeRequestWrapper(ctx, 3, "") modules := stdlib.GetModuleMap( // `os` is excluded, should *not* be importable from script. "math", "text", "times", "rand", "fmt", "json", "base64", "hex", "enum", ) - modules.AddBuiltinModule("http", createHTTPModule()) + modules.AddBuiltinModule("http", HTTPModule) s.SetImports(modules) s.SetMaxAllocs(maxAllocs) s.SetMaxConstObjects(maxConsts) @@ -37,65 +48,80 @@ func NewSecureScript(input []byte, globals map[string]interface{}) (*tengo.Scrip return s, nil } -func createHTTPModule() map[string]tengo.Object { - return map[string]tengo.Object{ - "get": &tengo.UserFunction{ - Name: "get", - Value: func(args ...tengo.Object) (tengo.Object, error) { - if len(args) < 1 || len(args) > 2 { - return nil, fmt.Errorf("expected 1 or 2 arguments, got %d", len(args)) - } - - url, ok := tengo.ToString(args[0]) - if !ok { - return nil, fmt.Errorf("expected argument 1 (URL) to be a string") - } - - headers := make(map[string]string) - if len(args) == 2 { - headerMap, ok := args[1].(*tengo.Map) - if !ok { - return nil, fmt.Errorf("expected argument 2 (headers) to be a map") - } - for key, value := range headerMap.Value { - strValue, valueOk := tengo.ToString(value) - if !valueOk { - return nil, fmt.Errorf("header values must be strings") - } - headers[key] = strValue - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, err - } - - for key, value := range headers { - req.Header.Add(key, value) - } - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - return &tengo.Map{ - Value: map[string]tengo.Object{ - "body": &tengo.String{Value: string(body)}, - "code": &tengo.Int{Value: int64(resp.StatusCode)}, - }, - }, nil - }, - }, +var httpGetFunction = &tengo.UserFunction{ + Name: "get", + Value: func(args ...tengo.Object) (tengo.Object, error) { + url, err := extractURL(args) + if err != nil { + return nil, err + } + headers, err := extractHeaders(args) + if err != nil { + return nil, err + } + + return performGetRequest(url, headers, defaultTimeout) + }, +} + +func extractURL(args []tengo.Object) (string, error) { + if len(args) < 1 { + return "", errors.New("expected at least 1 argument (URL)") + } + url, ok := tengo.ToString(args[0]) + if !ok { + return "", errors.New("expected argument 1 (URL) to be a string") + } + + return url, nil +} + +func extractHeaders(args []tengo.Object) (map[string]string, error) { + headers := make(map[string]string) + if len(args) == expectedArgsLength { + headerMap, ok := args[1].(*tengo.Map) + if !ok { + return nil, fmt.Errorf("expected argument %d (headers) to be a map", expectedArgsLength) + } + for key, value := range headerMap.Value { + strValue, valueOk := tengo.ToString(value) + if !valueOk { + return nil, fmt.Errorf("header value for key '%s' must be a string", key) + } + headers[key] = strValue + } + } + + return headers, nil +} + +func performGetRequest(url string, headers map[string]string, timeout time.Duration) (tengo.Object, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, err } + for key, value := range headers { + req.Header.Add(key, value) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return &tengo.Map{ + Value: map[string]tengo.Object{ + "body": &tengo.String{Value: string(body)}, + "code": &tengo.Int{Value: int64(resp.StatusCode)}, + }, + }, nil } From caefacf63940519c9379307134dfb6ed297a9730 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 13:43:04 +0530 Subject: [PATCH 10/19] removed redundant line --- plugins/internal/tengoutil/secure_script.go | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 55d74221b..cae4c7c91 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -29,7 +29,6 @@ var HTTPModule = map[string]tengo.Object{ func NewSecureScript(input []byte, globals map[string]interface{}) (*tengo.Script, error) { s := tengo.NewScript(input) - val := executeRequestWrapper(ctx, 3, "") modules := stdlib.GetModuleMap( // `os` is excluded, should *not* be importable from script. "math", "text", "times", "rand", "fmt", "json", "base64", "hex", "enum", From 5878e3fdd696688a2a757fc818f01c25bab8b216 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 14:22:25 +0530 Subject: [PATCH 11/19] check unit test --- .../internal/tengoutil/secure_script_test.go | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index b4c50bd04..b1219b23b 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -66,4 +66,62 @@ func TestNewSecureScript(t *testing.T) { _, err = s.Compile() assert.NoError(t, err) }) + + t.Run("HTTP GET with headers", func(t *testing.T) { + s, err := NewSecureScript([]byte(heredoc.Doc(` + http := import("http") + headers := { "User-Agent": "test-agent", "Accept": "application/json" } + response := http.get("http://example.com", headers) + response.body + `)), nil) + assert.NoError(t, err) + _, err = s.Compile() + assert.NoError(t, err) + }) + + t.Run("HTTP GET with invalid URL argument type", func(t *testing.T) { + s, err := NewSecureScript([]byte(heredoc.Doc(` + http := import("http") + http.get(12345) + `)), nil) + assert.NoError(t, err) + compiledScript, err := s.Compile() + assert.NoError(t, err) + + _, err = compiledScript.Run() + assert.Error(t, err) + assert.Contains(t, err.Error(), "expected argument 1 (URL) to be a string") + }) + + t.Run("HTTP GET with invalid header value type", func(t *testing.T) { + s, err := NewSecureScript([]byte(heredoc.Doc(` + http := import("http") + headers := { "User-Agent": 12345 } + http.get("http://example.com", headers) + `)), nil) + assert.NoError(t, err) + compiledScript, err := s.Compile() + assert.NoError(t, err) + + _, err = compiledScript.Run() + assert.Error(t, err) + assert.Contains(t, err.Error(), "header value for key 'User-Agent' must be a string") + }) + + t.Run("HTTP GET with timeout", func(t *testing.T) { + s, err := NewSecureScript([]byte(heredoc.Doc(` + http := import("http") + response := http.get("http://example.com") + response.body + `)), nil) + assert.NoError(t, err) + + defaultTimeout = 1 * time.Millisecond + compiledScript, err := s.Compile() + assert.NoError(t, err) + + _, err = compiledScript.Run() + assert.Error(t, err) + assert.Contains(t, err.Error(), "context deadline exceeded") + }) } From f964f6fb94df0f9f64230d78c0bdd2a53e555752 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 14:34:02 +0530 Subject: [PATCH 12/19] fix check unit tests --- .../internal/tengoutil/secure_script_test.go | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index b1219b23b..d9bb4a12e 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -5,6 +5,7 @@ package tengoutil import ( "testing" + "time" "github.com/MakeNowJust/heredoc" "github.com/stretchr/testify/assert" @@ -68,59 +69,64 @@ func TestNewSecureScript(t *testing.T) { }) t.Run("HTTP GET with headers", func(t *testing.T) { - s, err := NewSecureScript([]byte(heredoc.Doc(` + s, err := NewSecureScript(([]byte)(heredoc.Doc(` http := import("http") headers := { "User-Agent": "test-agent", "Accept": "application/json" } response := http.get("http://example.com", headers) response.body `)), nil) assert.NoError(t, err) - _, err = s.Compile() + + err = s.Compile() assert.NoError(t, err) }) t.Run("HTTP GET with invalid URL argument type", func(t *testing.T) { - s, err := NewSecureScript([]byte(heredoc.Doc(` + s, err := NewSecureScript(([]byte)(heredoc.Doc(` http := import("http") - http.get(12345) + http.get(12345) `)), nil) assert.NoError(t, err) - compiledScript, err := s.Compile() + + err = s.Compile() assert.NoError(t, err) - _, err = compiledScript.Run() + err = s.Run() assert.Error(t, err) assert.Contains(t, err.Error(), "expected argument 1 (URL) to be a string") }) t.Run("HTTP GET with invalid header value type", func(t *testing.T) { - s, err := NewSecureScript([]byte(heredoc.Doc(` + s, err := NewSecureScript(([]byte)(heredoc.Doc(` http := import("http") headers := { "User-Agent": 12345 } http.get("http://example.com", headers) `)), nil) assert.NoError(t, err) - compiledScript, err := s.Compile() + + err = s.Compile() assert.NoError(t, err) - _, err = compiledScript.Run() + err = s.Run() assert.Error(t, err) assert.Contains(t, err.Error(), "header value for key 'User-Agent' must be a string") }) t.Run("HTTP GET with timeout", func(t *testing.T) { - s, err := NewSecureScript([]byte(heredoc.Doc(` + s, err := NewSecureScript(([]byte)(heredoc.Doc(` http := import("http") response := http.get("http://example.com") response.body `)), nil) assert.NoError(t, err) - defaultTimeout = 1 * time.Millisecond - compiledScript, err := s.Compile() + defaultTimeout := 1 * time.Millisecond + defer func() { defaultTimeout = 5 * time.Second }() + + err = s.Compile() assert.NoError(t, err) - _, err = compiledScript.Run() + err = s.Run() assert.Error(t, err) assert.Contains(t, err.Error(), "context deadline exceeded") }) From c36b8d3c2cefbba337cac68674d2654fdc259cf8 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 14:40:15 +0530 Subject: [PATCH 13/19] fix return values for unit test --- .../internal/tengoutil/secure_script_test.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index d9bb4a12e..800a0828f 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -77,7 +77,7 @@ func TestNewSecureScript(t *testing.T) { `)), nil) assert.NoError(t, err) - err = s.Compile() + _, err = s.Compile() assert.NoError(t, err) }) @@ -88,10 +88,10 @@ func TestNewSecureScript(t *testing.T) { `)), nil) assert.NoError(t, err) - err = s.Compile() + _, err = s.Compile() assert.NoError(t, err) - err = s.Run() + _, err = s.Run() assert.Error(t, err) assert.Contains(t, err.Error(), "expected argument 1 (URL) to be a string") }) @@ -104,10 +104,10 @@ func TestNewSecureScript(t *testing.T) { `)), nil) assert.NoError(t, err) - err = s.Compile() + _, err = s.Compile() assert.NoError(t, err) - err = s.Run() + _, err = s.Run() assert.Error(t, err) assert.Contains(t, err.Error(), "header value for key 'User-Agent' must be a string") }) @@ -120,13 +120,14 @@ func TestNewSecureScript(t *testing.T) { `)), nil) assert.NoError(t, err) - defaultTimeout := 1 * time.Millisecond - defer func() { defaultTimeout = 5 * time.Second }() + originalTimeout := defaultTimeout + defaultTimeout = 1 * time.Millisecond + defer func() { defaultTimeout = originalTimeout }() - err = s.Compile() + _, err = s.Compile() assert.NoError(t, err) - err = s.Run() + _, err = s.Run() assert.Error(t, err) assert.Contains(t, err.Error(), "context deadline exceeded") }) From 1f86c5a57384b3842c48edfd1e239527365d8888 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 14:49:13 +0530 Subject: [PATCH 14/19] fix timeout unit test --- plugins/internal/tengoutil/secure_script.go | 7 +++---- plugins/internal/tengoutil/secure_script_test.go | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index cae4c7c91..835992156 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -17,10 +17,9 @@ const ( maxConsts = 500 ) -const ( - expectedArgsLength = 2 - defaultTimeout = 5 * time.Second -) +const expectedArgsLength = 2 + +var defaultTimeout = 5 * time.Second var HTTPModule = map[string]tengo.Object{ "get": httpGetFunction, diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index 800a0828f..5494c4447 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -113,11 +113,11 @@ func TestNewSecureScript(t *testing.T) { }) t.Run("HTTP GET with timeout", func(t *testing.T) { - s, err := NewSecureScript(([]byte)(heredoc.Doc(` + s, err := NewSecureScript([]byte(` http := import("http") response := http.get("http://example.com") response.body - `)), nil) + `), nil) assert.NoError(t, err) originalTimeout := defaultTimeout From 3cd8b3f2dcd0fef48a3313cd80a4941332278fdb Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 14:55:50 +0530 Subject: [PATCH 15/19] small typo fix --- plugins/internal/tengoutil/secure_script_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index 5494c4447..800a0828f 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -113,11 +113,11 @@ func TestNewSecureScript(t *testing.T) { }) t.Run("HTTP GET with timeout", func(t *testing.T) { - s, err := NewSecureScript([]byte(` + s, err := NewSecureScript(([]byte)(heredoc.Doc(` http := import("http") response := http.get("http://example.com") response.body - `), nil) + `)), nil) assert.NoError(t, err) originalTimeout := defaultTimeout From f351ff94837725999438ebf2629e582f43edc59e Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 15:08:00 +0530 Subject: [PATCH 16/19] fix check tests --- plugins/internal/tengoutil/secure_script.go | 2 +- plugins/internal/tengoutil/secure_script_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 835992156..97e35ce41 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -84,7 +84,7 @@ func extractHeaders(args []tengo.Object) (map[string]string, error) { for key, value := range headerMap.Value { strValue, valueOk := tengo.ToString(value) if !valueOk { - return nil, fmt.Errorf("header value for key '%s' must be a string", key) + return nil, fmt.Errorf("header value for key '%s' must be a string, got %T", key, value) } headers[key] = strValue } diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index 800a0828f..473f2597d 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -93,15 +93,15 @@ func TestNewSecureScript(t *testing.T) { _, err = s.Run() assert.Error(t, err) - assert.Contains(t, err.Error(), "expected argument 1 (URL) to be a string") + assert.Contains(t, err.Error(), "unsupported protocol scheme") }) t.Run("HTTP GET with invalid header value type", func(t *testing.T) { - s, err := NewSecureScript(([]byte)(heredoc.Doc(` + s, err := NewSecureScript([]byte(` http := import("http") headers := { "User-Agent": 12345 } http.get("http://example.com", headers) - `)), nil) + `), nil) assert.NoError(t, err) _, err = s.Compile() @@ -109,7 +109,7 @@ func TestNewSecureScript(t *testing.T) { _, err = s.Run() assert.Error(t, err) - assert.Contains(t, err.Error(), "header value for key 'User-Agent' must be a string") + assert.Contains(t, err.Error(), "header value for key 'User-Agent' must be a string, got int") }) t.Run("HTTP GET with timeout", func(t *testing.T) { From 50f3c8e784edd0687b90bbec19c9c38378cd80b6 Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 15:13:54 +0530 Subject: [PATCH 17/19] fixed test for header value type --- .../internal/tengoutil/secure_script_test.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index 473f2597d..162ed17cd 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -96,20 +96,26 @@ func TestNewSecureScript(t *testing.T) { assert.Contains(t, err.Error(), "unsupported protocol scheme") }) - t.Run("HTTP GET with invalid header value type", func(t *testing.T) { - s, err := NewSecureScript([]byte(` + t.Run("HTTP GET with valid header value type (int)", func(t *testing.T) { + s, err := NewSecureScript(([]byte)(heredoc.Doc(` http := import("http") headers := { "User-Agent": 12345 } http.get("http://example.com", headers) - `), nil) + `)), nil) assert.NoError(t, err) _, err = s.Compile() assert.NoError(t, err) - _, err = s.Run() - assert.Error(t, err) - assert.Contains(t, err.Error(), "header value for key 'User-Agent' must be a string, got int") + result, err := s.Run() + assert.NoError(t, err) + + resultMap, ok := result.(*tengo.Map) + assert.True(t, ok) + + userAgent, ok := resultMap.Value["User-Agent"].(*tengo.String) + assert.True(t, ok) + assert.Equal(t, "12345", userAgent.Value) }) t.Run("HTTP GET with timeout", func(t *testing.T) { From 2ede298d3cb7383e29c69cb211c22ee6f371291c Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 15:20:39 +0530 Subject: [PATCH 18/19] fixed test for header value type --- .../internal/tengoutil/secure_script_test.go | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script_test.go b/plugins/internal/tengoutil/secure_script_test.go index 162ed17cd..c2ea06edc 100644 --- a/plugins/internal/tengoutil/secure_script_test.go +++ b/plugins/internal/tengoutil/secure_script_test.go @@ -96,28 +96,6 @@ func TestNewSecureScript(t *testing.T) { assert.Contains(t, err.Error(), "unsupported protocol scheme") }) - t.Run("HTTP GET with valid header value type (int)", func(t *testing.T) { - s, err := NewSecureScript(([]byte)(heredoc.Doc(` - http := import("http") - headers := { "User-Agent": 12345 } - http.get("http://example.com", headers) - `)), nil) - assert.NoError(t, err) - - _, err = s.Compile() - assert.NoError(t, err) - - result, err := s.Run() - assert.NoError(t, err) - - resultMap, ok := result.(*tengo.Map) - assert.True(t, ok) - - userAgent, ok := resultMap.Value["User-Agent"].(*tengo.String) - assert.True(t, ok) - assert.Equal(t, "12345", userAgent.Value) - }) - t.Run("HTTP GET with timeout", func(t *testing.T) { s, err := NewSecureScript(([]byte)(heredoc.Doc(` http := import("http") From e1f2dd45bb3d6581542f3400f9b40f1b39be8f1f Mon Sep 17 00:00:00 2001 From: Sumeet Rai Date: Thu, 14 Nov 2024 16:59:09 +0530 Subject: [PATCH 19/19] httpModule export not required --- plugins/internal/tengoutil/secure_script.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/internal/tengoutil/secure_script.go b/plugins/internal/tengoutil/secure_script.go index 97e35ce41..345e5af61 100644 --- a/plugins/internal/tengoutil/secure_script.go +++ b/plugins/internal/tengoutil/secure_script.go @@ -21,7 +21,7 @@ const expectedArgsLength = 2 var defaultTimeout = 5 * time.Second -var HTTPModule = map[string]tengo.Object{ +var httpModule = map[string]tengo.Object{ "get": httpGetFunction, } @@ -32,7 +32,7 @@ func NewSecureScript(input []byte, globals map[string]interface{}) (*tengo.Scrip // `os` is excluded, should *not* be importable from script. "math", "text", "times", "rand", "fmt", "json", "base64", "hex", "enum", ) - modules.AddBuiltinModule("http", HTTPModule) + modules.AddBuiltinModule("http", httpModule) s.SetImports(modules) s.SetMaxAllocs(maxAllocs) s.SetMaxConstObjects(maxConsts)