grafana/pkg/services/live/pipeline/goja_expression.go

98 lines
2.1 KiB
Go

package pipeline
import (
"errors"
"fmt"
"time"
"github.com/dop251/goja"
"github.com/dop251/goja/parser"
)
func getRuntime(payload []byte) (*gojaRuntime, error) {
vm := goja.New()
vm.SetMaxCallStackSize(64)
vm.SetParserOptions(parser.WithDisableSourceMaps)
r := &gojaRuntime{vm}
err := r.init(payload)
if err != nil {
return nil, err
}
return r, nil
}
type gojaRuntime struct {
vm *goja.Runtime
}
// Parse JSON once.
func (r *gojaRuntime) init(payload []byte) error {
err := r.vm.Set("__body", string(payload))
if err != nil {
return err
}
_, err = r.runString(`var x = JSON.parse(__body)`)
return err
}
func (r *gojaRuntime) runString(script string) (goja.Value, error) {
doneCh := make(chan struct{})
go func() {
select {
case <-doneCh:
return
case <-time.After(100 * time.Millisecond):
// Some ideas to prevent misuse of scripts:
// * parse/validate scripts on save
// * block scripts after several timeouts in a row
// * block scripts on malformed returned error
// * limit total quota of time for scripts
// * maybe allow only one statement, reject scripts with cycles and functions.
r.vm.Interrupt(errors.New("timeout"))
}
}()
defer close(doneCh)
return r.vm.RunString(script)
}
func (r *gojaRuntime) getBool(script string) (bool, error) {
v, err := r.runString(script)
if err != nil {
return false, err
}
num, ok := v.Export().(bool)
if !ok {
return false, errors.New("unexpected return value")
}
return num, nil
}
func (r *gojaRuntime) getString(script string) (string, error) {
v, err := r.runString(script)
if err != nil {
return "", err
}
exportedVal := v.Export()
stringVal, ok := exportedVal.(string)
if !ok {
return "", fmt.Errorf("unexpected return value: %v (%T), script: %s", exportedVal, exportedVal, script)
}
return stringVal, nil
}
func (r *gojaRuntime) getFloat64(script string) (float64, error) {
v, err := r.runString(script)
if err != nil {
return 0, err
}
exported := v.Export()
switch v := exported.(type) {
case float64:
return v, nil
case int64:
return float64(v), nil
default:
return 0, fmt.Errorf("unexpected return value: %T", exported)
}
}