diff --git a/internal/dynamo-browse/controllers/scripts.go b/internal/dynamo-browse/controllers/scripts.go index 2bb24fc..ccbf941 100644 --- a/internal/dynamo-browse/controllers/scripts.go +++ b/internal/dynamo-browse/controllers/scripts.go @@ -61,13 +61,6 @@ func (sc *ScriptController) Init() { } else { log.Printf("warn: script lookup paths are invalid: %v", err) } - sc.scriptManager.SetDefaultOptions(scriptmanager.Options{ - OSExecShell: "/bin/bash", - Permissions: scriptmanager.Permissions{ - AllowShellCommands: true, - AllowEnv: true, - }, - }) } func (sc *ScriptController) SetMessageSender(sendMsg func(msg tea.Msg)) { diff --git a/internal/dynamo-browse/services/scriptmanager/modext.go b/internal/dynamo-browse/services/scriptmanager/modext.go index f08051a..0c858cf 100644 --- a/internal/dynamo-browse/services/scriptmanager/modext.go +++ b/internal/dynamo-browse/services/scriptmanager/modext.go @@ -52,7 +52,6 @@ func (m *extModule) command(ctx context.Context, args ...object.Object) object.O } newEnv := thisEnv - newEnv.options = m.scriptPlugin.scriptService.options ctx = ctxWithScriptEnv(ctx, newEnv) res, err := callFn(ctx, fnRes, objArgs) @@ -114,7 +113,6 @@ func (m *extModule) keyBinding(ctx context.Context, args ...object.Object) objec } newEnv := thisEnv - newEnv.options = m.scriptPlugin.scriptService.options ctx = ctxWithScriptEnv(ctx, newEnv) res, err := callFn(ctx, fnRes, objArgs) diff --git a/internal/dynamo-browse/services/scriptmanager/modos.go b/internal/dynamo-browse/services/scriptmanager/modos.go deleted file mode 100644 index a89d130..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modos.go +++ /dev/null @@ -1,64 +0,0 @@ -package scriptmanager - -import ( - "context" - "github.com/risor-io/risor/object" - "os" - "os/exec" -) - -type osModule struct { -} - -func (om *osModule) exec(ctx context.Context, args ...object.Object) object.Object { - if err := require("os.exec", 1, args); err != nil { - return err - } - - cmdExec, objErr := object.AsString(args[0]) - if objErr != nil { - return objErr - } - - opts := scriptEnvFromCtx(ctx).options - if !opts.Permissions.AllowShellCommands { - return object.Errorf("permission error: no permission to shell out") - } - - cmd := exec.Command(opts.OSExecShell, "-c", cmdExec) - out, err := cmd.Output() - if err != nil { - return object.NewError(err) - } - - return object.NewString(string(out)) -} - -func (om *osModule) env(ctx context.Context, args ...object.Object) object.Object { - if err := require("os.env", 1, args); err != nil { - return err - } - - cmdEnvName, objErr := object.AsString(args[0]) - if objErr != nil { - return objErr - } - - opts := scriptEnvFromCtx(ctx).options - if !opts.Permissions.AllowEnv { - return object.Nil - } - - envVal, hasVal := os.LookupEnv(cmdEnvName) - if !hasVal { - return object.Nil - } - return object.NewString(envVal) -} - -func (om *osModule) register() *object.Module { - return object.NewBuiltinsModule("os", map[string]object.Object{ - "exec": object.NewBuiltin("exec", om.exec), - "env": object.NewBuiltin("env", om.env), - }) -} diff --git a/internal/dynamo-browse/services/scriptmanager/modos_test.go b/internal/dynamo-browse/services/scriptmanager/modos_test.go index 49d2edf..455125b 100644 --- a/internal/dynamo-browse/services/scriptmanager/modos_test.go +++ b/internal/dynamo-browse/services/scriptmanager/modos_test.go @@ -15,49 +15,16 @@ func TestOSModule_Env(t *testing.T) { t.Setenv("EMPTY_VALUE", "") testFS := testScriptFile(t, "test.tm", ` - assert(os.env("FULL_VALUE") == "this is a value") - assert(os.env("EMPTY_VALUE") == "") - assert(os.env("MISSING_VALUE") == nil) + assert(os.getenv("FULL_VALUE") == "this is a value") + assert(os.getenv("EMPTY_VALUE") == "") + assert(os.getenv("MISSING_VALUE") == "") - assert(bool(os.env("FULL_VALUE")) == true) - assert(bool(os.env("EMPTY_VALUE")) == false) - assert(bool(os.env("MISSING_VALUE")) == false) + assert(bool(os.getenv("FULL_VALUE")) == true) + assert(bool(os.getenv("EMPTY_VALUE")) == false) + assert(bool(os.getenv("MISSING_VALUE")) == false) `) srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetDefaultOptions(scriptmanager.Options{ - OSExecShell: "/bin/bash", - Permissions: scriptmanager.Permissions{ - AllowEnv: true, - }, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - }) - - t.Run("should return nil when no access to environment variables", func(t *testing.T) { - t.Setenv("FULL_VALUE", "this is a value") - t.Setenv("EMPTY_VALUE", "") - - testFS := testScriptFile(t, "test.tm", ` - assert(os.env("FULL_VALUE") == nil) - assert(os.env("EMPTY_VALUE") == nil) - assert(os.env("MISSING_VALUE") == nil) - - assert(bool(os.env("FULL_VALUE")) == false) - assert(bool(os.env("EMPTY_VALUE")) == false) - assert(bool(os.env("MISSING_VALUE")) == false) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetDefaultOptions(scriptmanager.Options{ - OSExecShell: "/bin/bash", - Permissions: scriptmanager.Permissions{ - AllowEnv: false, - }, - }) ctx := context.Background() err := <-srv.RunAdHocScript(ctx, "test.tm") @@ -71,17 +38,11 @@ func TestOSModule_Exec(t *testing.T) { mockedUIService.EXPECT().PrintMessage(mock.Anything, "hello world\n") testFS := testScriptFile(t, "test.tm", ` - res := os.exec('echo "hello world"') + res := exec('echo', ["hello world"]).stdout ui.print(res) `) srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetDefaultOptions(scriptmanager.Options{ - OSExecShell: "/bin/bash", - Permissions: scriptmanager.Permissions{ - AllowShellCommands: true, - }, - }) srv.SetIFaces(scriptmanager.Ifaces{ UI: mockedUIService, }) @@ -92,72 +53,4 @@ func TestOSModule_Exec(t *testing.T) { mockedUIService.AssertExpectations(t) }) - - t.Run("should refuse to execute command if do not have permissions", func(t *testing.T) { - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "failed") - - testFS := testScriptFile(t, "test.tm", ` - res := try(func() { return os.exec('echo "hello world"') }, "failed") - ui.print(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetDefaultOptions(scriptmanager.Options{ - OSExecShell: "/bin/bash", - Permissions: scriptmanager.Permissions{ - AllowShellCommands: false, - }, - }) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - }) - - t.Run("should be able to change permissions which will affect plugins", func(t *testing.T) { - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Loaded the plugin\n") - - testFS := testScriptFile(t, "test.tm", ` - ext.command("mycommand", func() { - ui.print(os.exec('echo "this cannot run"')) - }) - - ui.print(os.exec('echo "Loaded the plugin"')) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetDefaultOptions(scriptmanager.Options{ - OSExecShell: "/bin/bash", - Permissions: scriptmanager.Permissions{ - AllowShellCommands: true, - }, - }) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - ctx := context.Background() - _, err := srv.LoadScript(ctx, "test.tm") - assert.NoError(t, err) - - srv.SetDefaultOptions(scriptmanager.Options{ - OSExecShell: "/bin/bash", - Permissions: scriptmanager.Permissions{ - AllowShellCommands: false, - }, - }) - - errChan := make(chan error) - assert.NoError(t, srv.LookupCommand("mycommand").Invoke(ctx, []string{}, errChan)) - assert.Error(t, waitForErr(t, errChan)) - - mockedUIService.AssertExpectations(t) - }) } diff --git a/internal/dynamo-browse/services/scriptmanager/opts.go b/internal/dynamo-browse/services/scriptmanager/opts.go index 9ef4408..39d961f 100644 --- a/internal/dynamo-browse/services/scriptmanager/opts.go +++ b/internal/dynamo-browse/services/scriptmanager/opts.go @@ -2,44 +2,12 @@ package scriptmanager import ( "context" - "os" - "github.com/risor-io/risor/limits" ) -type Options struct { - // OSExecShell is the shell to use for calls to 'os.exec'. If not defined, - // it will use the value of the SHELL environment variable, otherwise it will - // default to '/bin/bash' - OSExecShell string - - // Permissions are the permissions the script can execute in - Permissions Permissions -} - -func (opts Options) configuredShell() string { - if opts.OSExecShell != "" { - return opts.OSExecShell - } - if shell, hasShell := os.LookupEnv("SHELL"); hasShell { - return shell - } - return "/bin/bash" -} - -// Permissions control the set of permissions of a script -type Permissions struct { - // AllowShellCommands determines whether or not a script can execute shell commands. - AllowShellCommands bool - - // AllowEnv determines whether or not a script can access environment variables - AllowEnv bool -} - // scriptEnv is the runtime environment for a particular script execution type scriptEnv struct { filename string - options Options } type scriptEnvKeyType struct{} diff --git a/internal/dynamo-browse/services/scriptmanager/service.go b/internal/dynamo-browse/services/scriptmanager/service.go index d90629f..8e17ae0 100644 --- a/internal/dynamo-browse/services/scriptmanager/service.go +++ b/internal/dynamo-browse/services/scriptmanager/service.go @@ -17,7 +17,6 @@ import ( type Service struct { lookupPaths []fs.FS ifaces Ifaces - options Options sched *scriptScheduler plugins []*ScriptPlugin } @@ -37,10 +36,6 @@ func (s *Service) SetLookupPaths(fs []fs.FS) { s.lookupPaths = fs } -func (s *Service) SetDefaultOptions(options Options) { - s.options = options -} - func (s *Service) SetIFaces(ifaces Ifaces) { s.ifaces = ifaces } @@ -94,13 +89,10 @@ func (s *Service) startAdHocScript(ctx context.Context, filename string, errChan return } - ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename), options: s.options}) + ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename)}) if _, err := risor.Eval(ctx, code, risor.WithGlobals(s.builtins()), - // risor.WithDefaultBuiltins(), - // risor.WithDefaultModules(), - // risor.WithBuiltins(s.builtins()), ); err != nil { errChan <- errors.Wrapf(err, "script %v", filename) return @@ -126,12 +118,9 @@ func (s *Service) loadScript(ctx context.Context, filename string, resChan chan scriptService: s, } - ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename), options: s.options}) + ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename)}) if _, err := risor.Eval(ctx, code, - // risor.WithDefaultBuiltins(), - // risor.WithDefaultModules(), - // risor.WithBuiltins(s.builtins()), risor.WithGlobals(s.builtins()), risor.WithGlobals(map[string]any{ "ext": (&extModule{scriptPlugin: newPlugin}).register(), @@ -251,7 +240,6 @@ func (s *Service) builtins() map[string]any { return map[string]any{ "ui": (&uiModule{uiService: s.ifaces.UI}).register(), "session": (&sessionModule{sessionService: s.ifaces.Session}).register(), - "os": (&osModule{}).register(), "print": object.NewBuiltin("print", printBuiltin), "printf": object.NewBuiltin("printf", printfBuiltin), }