python.go 3.9 KB

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