This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-core/pkg/tools/execute/code.go

166 lines
4.3 KiB
Go

package execute
import (
"context"
"encoding/json"
"fmt"
"io"
"ollie/internal/sandbox"
"regexp"
"strings"
)
// execute_code is implemented directly by Executor.Execute with trusted=false.
// See executor.go for the implementation.
var dangerousPatterns = []*regexp.Regexp{
regexp.MustCompile(`rm\s+(-[a-z]*r[a-z]*\s+)*-[a-z]*f[a-z]*\s*/(home|var|usr|etc|boot|root|bin|sbin|lib|opt|srv)?`),
regexp.MustCompile(`rm\s+(-[a-z]*f[a-z]*\s+)*-[a-z]*r[a-z]*\s*/(home|var|usr|etc|boot|root|bin|sbin|lib|opt|srv)?`),
regexp.MustCompile(`rm\s+.*--recursive.*--force`),
regexp.MustCompile(`rm\s+.*--force.*--recursive`),
regexp.MustCompile(`rm\s+(-[a-z]*r[a-z]*\s+)*-[a-z]*f[a-z]*\s+\.\.?(/|$)`),
regexp.MustCompile(`rm\s+(-[a-z]*r[a-z]*\s+)*-[a-z]*f[a-z]*\s+~`),
regexp.MustCompile(`rm\s+(-[a-z]*r[a-z]*\s+)*-[a-z]*f[a-z]*\s+\*`),
regexp.MustCompile(`:\s*\(\s*\)\s*\{\s*:\s*\|\s*:\s*&`),
regexp.MustCompile(`\bmkfs\b`),
regexp.MustCompile(`\bdd\b.*\bif=/dev/`),
regexp.MustCompile(`>\s*/dev/sd`),
regexp.MustCompile(`\beval\s+".*\$`),
regexp.MustCompile(`\b(sudo|su)\s`),
regexp.MustCompile(`/etc/(shadow|sudoers)`),
}
// Dispatch routes a named execute tool call. Called by tools.BuiltinServer.
func (e *Server) Dispatch(ctx context.Context, name string, args json.RawMessage) (string, error) {
switch name {
case "execute_code":
return dispatchExecuteCode(ctx, e, args)
case "execute_tool":
return dispatchExecuteTool(ctx, e, args)
case "execute_pipe":
return dispatchExecutePipe(ctx, e, args)
default:
return "", fmt.Errorf("unknown execute tool: %s", name)
}
}
// ValidateCode checks code against dangerous patterns.
func (e *Server) ValidateCode(code string) error {
if err := e.checkRateLimit(); err != nil {
return err
}
normalized := strings.ToLower(code)
normalized = whitespacePattern.ReplaceAllString(normalized, " ")
for _, pattern := range dangerousPatterns {
if pattern.MatchString(normalized) {
e.recordValidationFailure()
return fmt.Errorf("dangerous pattern detected")
}
}
return nil
}
func loadLayeredConfig(name string) (*sandbox.Config, error) {
baseCfg, err := sandbox.Load()
if err != nil {
return nil, fmt.Errorf("failed to load global config: %w", err)
}
baseLayer := sandbox.LayeredConfig{
Filesystem: baseCfg.Filesystem,
Network: baseCfg.Network,
Env: baseCfg.Env,
}
layers := []sandbox.LayeredConfig{baseLayer}
if name == "" {
name = "default"
}
sbxLayer, err := sandbox.LoadSandbox(name)
if err != nil {
return nil, fmt.Errorf("failed to load sandbox %q: %w", name, err)
}
layers = append(layers, sbxLayer)
general := sandbox.GeneralConfig{
BestEffort: baseCfg.General.BestEffort,
LogLevel: baseCfg.General.LogLevel,
}
advanced := sandbox.AdvancedConfig{
LDD: baseCfg.Advanced.LDD,
AddExec: baseCfg.Advanced.AddExec,
}
return sandbox.Merge(general, advanced, layers...), nil
}
type limitedWriter struct {
w io.Writer
written int
limit int
truncated bool
}
func (lw *limitedWriter) Write(p []byte) (n int, err error) {
if lw.written >= lw.limit {
lw.truncated = true
return len(p), nil
}
remaining := lw.limit - lw.written
toWrite := p
if len(p) > remaining {
toWrite = p[:remaining]
lw.truncated = true
}
written, err := lw.w.Write(toWrite)
lw.written += written
if err != nil {
return written, err
}
return len(p), nil
}
func dispatchExecuteCode(ctx context.Context, e *Server, args json.RawMessage) (string, error) {
code, language, sandbox, timeout, err := execArgs(args)
if err != nil {
return "", fmt.Errorf("execute_code: bad args: %w", err)
}
if code == "" {
return "", fmt.Errorf("execute_code: 'code' is required")
}
if e.Confirm != nil && !e.Confirm(fmt.Sprintf("execute_code: %s", code)) {
return "", fmt.Errorf("execute_code: denied by user")
}
return e.Execute(ctx, code, language, timeout, sandbox, false)
}
func execArgs(args json.RawMessage) (code, language, sandbox string, timeout int, err error) {
var a struct {
Code string `json:"code"`
Language string `json:"language"`
Timeout int `json:"timeout"`
Sandbox string `json:"sandbox"`
}
if err = json.Unmarshal(args, &a); err != nil {
return
}
code = a.Code
language = a.Language
if language == "" {
language = "bash"
}
timeout = a.Timeout
if timeout <= 0 {
timeout = 30
}
sandbox = a.Sandbox
if sandbox == "" {
sandbox = "default"
}
return
}