Unverified Commit 80662679 authored by Simon Esposito's avatar Simon Esposito Committed by GitHub
Browse files

Make the JS global object immutable (#765)



Prevent global variables and registered functions/objects from being reset in the JavaScript vms.
Add config flag to allow disabling immutability.
Upgrade goja to latest
Resolves #550

Co-authored-by: default avatarAndrei Mihu <andrei@heroiclabs.com>
parent d0bb6d5f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr
- Allow migrate subcommand to use database names that contain dashes.
- Add senderID param to channelIdBuild function.

### Changed
- JavaScript global variables are made immutable after the `InitModule` function is invoked.

### Fixed
- Fix the registered function name for 'nk.channelIdBuild' in the JavaScript runtime.
- Better input validation for Steam link operations.
+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ require (
	github.com/blugelabs/bluge v0.1.8
	github.com/blugelabs/bluge_segment_api v0.2.0
	github.com/blugelabs/query_string v0.3.0
	github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06
	github.com/dop251/goja v0.0.0-20220124171016-cfb079cdc7b4
	github.com/gofrs/uuid v4.0.0+incompatible
	github.com/golang-jwt/jwt/v4 v4.1.0
	github.com/gorilla/handlers v1.5.1
+2 −0
Original line number Diff line number Diff line
@@ -132,6 +132,8 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06 h1:XqC5eocqw7r3+HOhKYqaYH07XBiBDp9WE3NQK8XHSn4=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20220124171016-cfb079cdc7b4 h1:gUXabLfCUjaNl7kLxGdaZaw1c5x33SGL9PEo6p/hfuo=
github.com/dop251/goja v0.0.0-20220124171016-cfb079cdc7b4/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+2 −0
Original line number Diff line number Diff line
@@ -778,6 +778,7 @@ type RuntimeConfig struct {
	EventQueueWorkers  int               `yaml:"event_queue_workers" json:"event_queue_workers" usage:"Number of workers to use for concurrent processing of events. Default 8."`
	ReadOnlyGlobals    bool              `yaml:"read_only_globals" json:"read_only_globals" usage:"When enabled marks all Lua runtime global tables as read-only to reduce memory footprint. Default true."` // Kept for backwards compatibility
	LuaReadOnlyGlobals bool              `yaml:"lua_read_only_globals" json:"lua_read_only_globals" usage:"When enabled marks all Lua runtime global tables as read-only to reduce memory footprint. Default true."`
	JsReadOnlyGlobals  bool              `yaml:"js_read_only_globals" json:"js_read_only_globals" usage:"When enabled marks all Javascript runtime globals as read-only to reduce memory footprint. Default true."`
	LuaApiStacktrace   bool              `yaml:"lua_api_stacktrace" json:"lua_api_stacktrace" usage:"Include the Lua stacktrace in error responses returned to the client. Default false."`
	JsEntrypoint       string            `yaml:"js_entrypoint" json:"js_entrypoint" usage:"Specifies the location of the bundled JavaScript runtime source code."`
}
@@ -839,6 +840,7 @@ func NewRuntimeConfig() *RuntimeConfig {
		EventQueueWorkers:  8,
		ReadOnlyGlobals:    true,
		LuaReadOnlyGlobals: true,
		JsReadOnlyGlobals:  true,
		LuaApiStacktrace:   false,
	}
}
+19 −0
Original line number Diff line number Diff line
@@ -1524,6 +1524,7 @@ func NewRuntimeProviderJS(logger, startupLogger *zap.Logger, db *sql.DB, protojs
		runtime := goja.New()

		runtime.RunProgram(modCache.Modules[modCache.Names[0]].Program)
		freezeGlobalObject(config, runtime)

		jsLoggerInst, err := NewJsLogger(runtime, logger)
		if err != nil {
@@ -1955,3 +1956,21 @@ func evalRuntimeModules(rp *RuntimeProviderJS, modCache *RuntimeJSModuleCache, m

	return initializer.Callbacks, initializer.MatchCallbacks, nil
}

// Equivalent to calling freeze on the JavaScript global object making it immutable
// https://github.com/dop251/goja/issues/362
func freezeGlobalObject(config Config, r *goja.Runtime) {
	if !config.GetRuntime().JsReadOnlyGlobals {
		return
	}
	r.RunString(`
for (const k of Reflect.ownKeys(globalThis)) {
    const v = globalThis[k];
    if (v) {
        Object.freeze(v);
        v.prototype && Object.freeze(v.prototype);
        v.__proto__ && Object.freeze(v.__proto__);
    }
}
`)
}
Loading