Removed internal os module
Module "os" is no longer needed since Risor comes with an "os" and "exec" module out of the box now.
This commit is contained in:
parent
ceb064a346
commit
12909c89ee
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
})
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue