From c32949c8e0e84476e5c308f8398954e6339ab7be Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Sat, 26 Sep 2026 01:17:49 +0400 Subject: [PATCH] fix(script): convert non-string header, param and cookie values to text --- docs/configuration.md | 4 ++-- internal/script/js.go | 36 +++++++++++++++++++++++++----------- internal/script/lua.go | 35 ++++++++++++++++++++++++----------- 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index bee01f9..d8df4f6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -456,7 +456,7 @@ function transform(req) end ``` -> **Note:** Header, parameter, and cookie values can be a single string or a table (array) for multiple values per key (e.g. `{"val1", "val2"}`). +> **Note:** Header, parameter, and cookie values can be a single string or a table (array) for multiple values per key (e.g. `{"val1", "val2"}`). Numbers and booleans are converted to text, other values are skipped. > **Note:** Lua scripts can send their own HTTP requests with `http` and work with JSON through the global `json` table. See [HTTP Requests in Scripts](#http-requests-in-scripts). @@ -523,7 +523,7 @@ function transform(req) { } ``` -> **Note:** Header, parameter, and cookie values can be a single string or an array for multiple values per key (e.g. `["val1", "val2"]`). +> **Note:** Header, parameter, and cookie values can be a single string or an array for multiple values per key (e.g. `["val1", "val2"]`). Numbers and booleans are converted to text, other values are skipped. > **Note:** JavaScript scripts can send their own HTTP requests with `http`. See [HTTP Requests in Scripts](#http-requests-in-scripts). diff --git a/internal/script/js.go b/internal/script/js.go index 721570a..734b225 100644 --- a/internal/script/js.go +++ b/internal/script/js.go @@ -2,6 +2,7 @@ package script import ( "errors" + "strconv" "github.com/dop251/goja" "go.aykhans.me/sarin/internal/types" @@ -195,22 +196,23 @@ func (e *JsEngine) objectToStringSliceMap(obj *goja.Object) map[string][]string result := make(map[string][]string) for _, key := range obj.Keys() { v := obj.Get(key) - if v == nil || goja.IsUndefined(v) || goja.IsNull(v) { - continue - } // Check if it's an array - if arr, ok := v.Export().([]any); ok { - var values []string - for _, item := range arr { - if s, ok := item.(string); ok { - values = append(values, s) + if arr, ok := v.(*goja.Object); ok && arr.ClassName() == "Array" { + length := int(arr.Get("length").ToInteger()) + values := make([]string, 0, length) + for i := range length { + if text, ok := jsStringValue(arr.Get(strconv.Itoa(i))); ok { + values = append(values, text) } } result[key] = values - } else { - // Single value, wrap it in a slice - result[key] = []string{v.String()} + continue + } + + // Single value, wrap it in a slice + if text, ok := jsStringValue(v); ok { + result[key] = []string{text} } } return result @@ -231,3 +233,15 @@ func (e *JsEngine) exceptionError(err error) error { } return errors.New(message) } + +// jsStringValue renders a JavaScript primitive the way the script would see it. +// Objects, arrays and functions have no useful text form, so they are skipped. +func jsStringValue(v goja.Value) (string, bool) { + if v == nil || goja.IsUndefined(v) || goja.IsNull(v) { + return "", false + } + if _, isObject := v.(*goja.Object); isObject { + return "", false + } + return v.String(), true +} diff --git a/internal/script/lua.go b/internal/script/lua.go index e6c2fd1..9956d44 100644 --- a/internal/script/lua.go +++ b/internal/script/lua.go @@ -168,20 +168,33 @@ func (e *LuaEngine) tableToStringSliceMap(t *lua.LTable) map[string][]string { } key := string(k.(lua.LString)) - switch v.Type() { - case lua.LTString: - // Single string value - result[key] = []string{string(v.(lua.LString))} - case lua.LTTable: - // Array of strings - var values []string - v.(*lua.LTable).ForEach(func(_, item lua.LValue) { - if item.Type() == lua.LTString { - values = append(values, string(item.(lua.LString))) + if v.Type() == lua.LTTable { + // Array of values, keys outside 1..n are skipped + arr := v.(*lua.LTable) + length := arr.Len() + values := make([]string, 0, length) + for i := 1; i <= length; i++ { + if text, ok := luaScalarString(arr.RawGetInt(i)); ok { + values = append(values, text) } - }) + } result[key] = values + return + } + + if text, ok := luaScalarString(v); ok { + result[key] = []string{text} } }) return result } + +// luaScalarString renders strings, numbers and booleans as text. +func luaScalarString(v lua.LValue) (string, bool) { + switch v.Type() { + case lua.LTString, lua.LTNumber, lua.LTBool: + return v.String(), true + default: + return "", false + } +}