python.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package python
  2. import (
  3. _ "embed"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/google/uuid"
  11. "github.com/langgenius/dify-sandbox/internal/core/runner"
  12. python_dependencies "github.com/langgenius/dify-sandbox/internal/core/runner/python/dependencies"
  13. "github.com/langgenius/dify-sandbox/internal/core/runner/types"
  14. "github.com/langgenius/dify-sandbox/internal/static"
  15. )
  16. type PythonRunner struct {
  17. runner.TempDirRunner
  18. }
  19. //go:embed prescript.py
  20. var python_sandbox_fs []byte
  21. var (
  22. PYTHON_REQUIRED_FS = []string{
  23. "/tmp/sandbox-python/python.so",
  24. "/etc/ssl/certs/ca-certificates.crt",
  25. "/usr/local/lib/python3.10/site-packages/certifi/cacert.pem",
  26. "/usr/local/lib/python3.10/dist-packages/certifi/cacert.pem",
  27. "/etc/nsswitch.conf",
  28. "/etc/resolv.conf",
  29. "/run/systemd/resolve/stub-resolv.conf",
  30. "/run/resolvconf/resolv.conf",
  31. "/etc/hosts",
  32. }
  33. )
  34. func (p *PythonRunner) Run(
  35. code string,
  36. timeout time.Duration,
  37. stdin []byte,
  38. preload string,
  39. options *types.RunnerOptions,
  40. ) (chan []byte, chan []byte, chan bool, error) {
  41. configuration := static.GetDifySandboxGlobalConfigurations()
  42. // initialize the environment
  43. untrusted_code_path, err := p.InitializeEnvironment(code, preload, options)
  44. if err != nil {
  45. return nil, nil, nil, err
  46. }
  47. // capture the output
  48. output_handler := runner.NewOutputCaptureRunner()
  49. output_handler.SetTimeout(timeout)
  50. err = p.WithTempDir(PYTHON_REQUIRED_FS, func(root_path string) error {
  51. // cleanup
  52. output_handler.SetAfterExitHook(func() {
  53. os.RemoveAll(root_path)
  54. os.Remove(root_path)
  55. })
  56. // create a new process
  57. cmd := exec.Command(
  58. configuration.PythonPath,
  59. untrusted_code_path,
  60. )
  61. cmd.Env = []string{}
  62. if configuration.Proxy.Socks5 != "" {
  63. cmd.Env = append(cmd.Env, fmt.Sprintf("HTTPS_PROXY=%s", configuration.Proxy.Socks5))
  64. cmd.Env = append(cmd.Env, fmt.Sprintf("HTTP_PROXY=%s", configuration.Proxy.Socks5))
  65. } else if configuration.Proxy.Https != "" || configuration.Proxy.Http != "" {
  66. if configuration.Proxy.Https != "" {
  67. cmd.Env = append(cmd.Env, fmt.Sprintf("HTTPS_PROXY=%s", configuration.Proxy.Https))
  68. }
  69. if configuration.Proxy.Http != "" {
  70. cmd.Env = append(cmd.Env, fmt.Sprintf("HTTP_PROXY=%s", configuration.Proxy.Http))
  71. }
  72. }
  73. err = output_handler.CaptureOutput(cmd)
  74. if err != nil {
  75. return err
  76. }
  77. return nil
  78. })
  79. if err != nil {
  80. return nil, nil, nil, err
  81. }
  82. return output_handler.GetStdout(), output_handler.GetStderr(), output_handler.GetDone(), nil
  83. }
  84. func (p *PythonRunner) InitializeEnvironment(code string, preload string, options *types.RunnerOptions) (string, error) {
  85. // create a tmp dir and copy the python script
  86. temp_code_name := strings.ReplaceAll(uuid.New().String(), "-", "_")
  87. temp_code_name = strings.ReplaceAll(temp_code_name, "/", ".")
  88. packages_preload := make([]string, len(options.Dependencies))
  89. for i, dependency := range options.Dependencies {
  90. packages_preload[i] = python_dependencies.GetDependencies(dependency.Name, dependency.Version)
  91. }
  92. script := strings.Replace(
  93. string(python_sandbox_fs),
  94. "{{uid}}", strconv.Itoa(static.SANDBOX_USER_UID), 1,
  95. )
  96. script = strings.Replace(
  97. script,
  98. "{{gid}}", strconv.Itoa(static.SANDBOX_GROUP_ID), 1,
  99. )
  100. if options.EnableNetwork {
  101. script = strings.Replace(
  102. script,
  103. "{{enable_network}}", "1", 1,
  104. )
  105. } else {
  106. script = strings.Replace(
  107. script,
  108. "{{enable_network}}", "0", 1,
  109. )
  110. }
  111. script = strings.Replace(
  112. script,
  113. "{{preload}}",
  114. fmt.Sprintf("%s\n%s", preload, strings.Join(packages_preload, "\n")),
  115. 1,
  116. )
  117. code = strings.Replace(
  118. script,
  119. "{{code}}",
  120. code,
  121. 1,
  122. )
  123. untrusted_code_path := fmt.Sprintf("/tmp/code/%s.py", temp_code_name)
  124. err := os.MkdirAll("/tmp/code", 0755)
  125. if err != nil {
  126. return "", err
  127. }
  128. err = os.WriteFile(untrusted_code_path, []byte(code), 0755)
  129. if err != nil {
  130. return "", err
  131. }
  132. return untrusted_code_path, nil
  133. }