@bniladridas/cursor 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.clang-tidy +28 -0
  2. package/.dockerignore +56 -0
  3. package/.env.example +29 -0
  4. package/.github/CODEOWNERS +2 -0
  5. package/.github/ISSUE_TEMPLATE/blank.md +27 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. package/.github/SECURITY.md +24 -0
  9. package/.github/codeql/codeql-config.yml +8 -0
  10. package/.github/dependabot.yml +14 -0
  11. package/.github/labeler.yml +50 -0
  12. package/.github/packaging/brand-cursor.png +0 -0
  13. package/.github/packaging/database/init.sql +48 -0
  14. package/.github/packaging/docker/Dockerfile +111 -0
  15. package/.github/packaging/docker/docker-compose.yml +56 -0
  16. package/.github/packaging/scripts/preflight.sh +413 -0
  17. package/.github/packaging/scripts/prepare-release.sh +141 -0
  18. package/.github/packaging/scripts/release.sh +22 -0
  19. package/.github/packaging/scripts/setup-git-hooks.sh +73 -0
  20. package/.github/pull_request_template.md +31 -0
  21. package/.github/signed.json +9 -0
  22. package/.github/workflows/README.md +23 -0
  23. package/.github/workflows/ci.yml +181 -0
  24. package/.github/workflows/cla.yml +33 -0
  25. package/.github/workflows/formula-sha.yml +63 -0
  26. package/.github/workflows/issue-response.yml +44 -0
  27. package/.github/workflows/labeler.yml +42 -0
  28. package/.github/workflows/pr-body.yml +49 -0
  29. package/.github/workflows/release.yml +176 -0
  30. package/.github/workflows/security.yml +94 -0
  31. package/.github/workflows/stale.yml +38 -0
  32. package/AGENTS.md +49 -0
  33. package/CHANGELOG.md +3 -0
  34. package/CMakeLists.txt +646 -0
  35. package/Formula/cursor.rb +46 -0
  36. package/LICENSE +201 -0
  37. package/Makefile +28 -0
  38. package/README.md +46 -0
  39. package/cli.js +16 -0
  40. package/include/agent.h +86 -0
  41. package/include/agent_mode.h +17 -0
  42. package/include/memory_manager.h +102 -0
  43. package/include/services/ai_service.h +31 -0
  44. package/include/services/auth_service.h +87 -0
  45. package/include/services/checkpoint_service.h +69 -0
  46. package/include/services/codebase_service.h +38 -0
  47. package/include/services/command_service.h +23 -0
  48. package/include/services/context_service.h +74 -0
  49. package/include/services/database_service.h +56 -0
  50. package/include/services/error_service.h +106 -0
  51. package/include/services/file_service.h +51 -0
  52. package/include/services/git_service.h +29 -0
  53. package/include/services/github_service.h +85 -0
  54. package/include/services/mcp_service.h +85 -0
  55. package/include/services/multi_file_service.h +93 -0
  56. package/include/services/sandbox_service.h +96 -0
  57. package/include/services/theme_service.h +67 -0
  58. package/include/services/web_service.h +52 -0
  59. package/include/utils/config.h +68 -0
  60. package/include/utils/memory_utils.h +79 -0
  61. package/include/utils/platform.h +56 -0
  62. package/include/utils/ui.h +43 -0
  63. package/include/utils/validation.h +63 -0
  64. package/include/utils/version.h.in +17 -0
  65. package/install.js +49 -0
  66. package/package.json +16 -0
  67. package/release/checksums.txt +3 -0
  68. package/release/cursor-linux/cursor_v0.1.7_linux_amd64.tar.gz +0 -0
  69. package/release/cursor-macos/cursor_v0.1.7_darwin_arm64.tar.gz +0 -0
  70. package/release/cursor-windows/cursor__windows_amd64.zip +0 -0
  71. package/src/agent.cpp +2026 -0
  72. package/src/main.cpp +97 -0
  73. package/src/memory_manager.cpp +814 -0
  74. package/src/services/ai_service.cpp +366 -0
  75. package/src/services/auth_service.cpp +779 -0
  76. package/src/services/checkpoint_service.cpp +465 -0
  77. package/src/services/codebase_service.cpp +233 -0
  78. package/src/services/command_service.cpp +82 -0
  79. package/src/services/context_service.cpp +348 -0
  80. package/src/services/database_service.cpp +148 -0
  81. package/src/services/error_service.cpp +438 -0
  82. package/src/services/file_service.cpp +349 -0
  83. package/src/services/git_service.cpp +148 -0
  84. package/src/services/github_service.cpp +435 -0
  85. package/src/services/mcp_service.cpp +481 -0
  86. package/src/services/multi_file_service.cpp +591 -0
  87. package/src/services/sandbox_service.cpp +678 -0
  88. package/src/services/theme_service.cpp +429 -0
  89. package/src/services/web_service.cpp +532 -0
  90. package/src/utils/config.cpp +77 -0
  91. package/src/utils/memory_utils.cpp +93 -0
  92. package/src/utils/ui.cpp +307 -0
  93. package/src/utils/validation.cpp +306 -0
  94. package/src/utils/version.cpp +175 -0
  95. package/tests/e2e/docker-compose.yml +195 -0
  96. package/tests/e2e/run_e2e_tests.sh +70 -0
  97. package/tests/e2e/run_tests_in_docker.sh +115 -0
  98. package/tests/main_test.cpp +16 -0
  99. package/tests/mocks/mock_ollama.py +98 -0
  100. package/tests/mocks/start_nginx.sh +64 -0
@@ -0,0 +1,678 @@
1
+ #include "services/sandbox_service.h"
2
+ #include "utils/platform.h"
3
+ #include "utils/validation.h"
4
+ #include <algorithm>
5
+ #include <chrono>
6
+ #include <cstdio>
7
+ #include <cstdlib>
8
+ #include <cstring>
9
+ #include <ctime>
10
+ #include <filesystem>
11
+ #include <fstream>
12
+ #include <iostream>
13
+ #include <random>
14
+ #include <regex>
15
+ #include <sstream>
16
+ #ifdef _WIN32
17
+ #include <windows.h>
18
+ #else
19
+ #include <sys/wait.h>
20
+ #include <unistd.h>
21
+ #endif
22
+ #include <nlohmann/json.hpp>
23
+
24
+ namespace Services {
25
+
26
+ std::map<std::string, SandboxConfig> SandboxService::sandbox_configs_;
27
+
28
+ std::string SandboxService::get_sandbox_config_path() {
29
+ return "data/sandbox_config.json";
30
+ }
31
+
32
+ void SandboxService::ensure_sandbox_directory() {
33
+ std::filesystem::create_directories("data");
34
+ std::filesystem::create_directories("data/sandbox");
35
+ }
36
+
37
+ void SandboxService::initialize_default_configs() {
38
+ // Default sandbox for general use
39
+ SandboxConfig default_config;
40
+ default_config.name = "default";
41
+ default_config.image = "ubuntu:22.04";
42
+ default_config.memory_limit_mb = 512;
43
+ default_config.cpu_limit_percent = 50;
44
+ default_config.timeout_seconds = 300;
45
+ default_config.network_access = false;
46
+ default_config.working_directory = "/workspace";
47
+ default_config.allowed_commands = {"ls", "cat", "echo", "grep",
48
+ "find", "head", "tail", "wc"};
49
+ sandbox_configs_["default"] = default_config;
50
+
51
+ // Python sandbox
52
+ SandboxConfig python_config;
53
+ python_config.name = "python";
54
+ python_config.image = "python:3.11-slim";
55
+ python_config.memory_limit_mb = 1024;
56
+ python_config.cpu_limit_percent = 75;
57
+ python_config.timeout_seconds = 600;
58
+ python_config.network_access = false;
59
+ python_config.working_directory = "/workspace";
60
+ python_config.allowed_commands = {"python", "python3", "pip",
61
+ "ls", "cat", "echo"};
62
+ sandbox_configs_["python"] = python_config;
63
+
64
+ // Node.js sandbox
65
+ SandboxConfig node_config;
66
+ node_config.name = "nodejs";
67
+ node_config.image = "node:18-slim";
68
+ node_config.memory_limit_mb = 1024;
69
+ node_config.cpu_limit_percent = 75;
70
+ node_config.timeout_seconds = 600;
71
+ node_config.network_access = false;
72
+ node_config.working_directory = "/workspace";
73
+ node_config.allowed_commands = {"node", "npm", "ls", "cat", "echo"};
74
+ sandbox_configs_["nodejs"] = node_config;
75
+
76
+ // Secure shell sandbox
77
+ SandboxConfig shell_config;
78
+ shell_config.name = "shell";
79
+ shell_config.image = "alpine:latest";
80
+ shell_config.memory_limit_mb = 256;
81
+ shell_config.cpu_limit_percent = 25;
82
+ shell_config.timeout_seconds = 120;
83
+ shell_config.network_access = false;
84
+ shell_config.working_directory = "/workspace";
85
+ shell_config.allowed_commands = {"sh", "ls", "cat", "echo",
86
+ "grep", "find", "head", "tail"};
87
+ sandbox_configs_["shell"] = shell_config;
88
+ }
89
+
90
+ std::string SandboxService::generate_container_name() {
91
+ static int counter = 1;
92
+ auto now = std::chrono::system_clock::now();
93
+ auto time_t = std::chrono::system_clock::to_time_t(now);
94
+
95
+ std::stringstream ss;
96
+ ss << "cursor_sandbox_" << time_t << "_" << counter++;
97
+ return ss.str();
98
+ }
99
+
100
+ bool SandboxService::is_docker_available() {
101
+ FILE *pipe = Utils::Platform::open_process("docker --version 2>&1", "r");
102
+ if (!pipe)
103
+ return false;
104
+
105
+ char buffer[128];
106
+ bool success = false;
107
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
108
+ if (strstr(buffer, "Docker version") != nullptr) {
109
+ success = true;
110
+ break;
111
+ }
112
+ }
113
+ Utils::Platform::close_process(pipe);
114
+ return success;
115
+ }
116
+
117
+ std::string SandboxService::escape_shell_arg(const std::string &arg) {
118
+ std::string escaped = arg;
119
+ // Basic shell escaping - replace single quotes
120
+ size_t pos = 0;
121
+ while ((pos = escaped.find("'", pos)) != std::string::npos) {
122
+ escaped.replace(pos, 1, "'\"'\"'");
123
+ pos += 5;
124
+ }
125
+ return "'" + escaped + "'";
126
+ }
127
+
128
+ void SandboxService::initialize() {
129
+ initialize_default_configs();
130
+ load_sandbox_config();
131
+ }
132
+
133
+ bool SandboxService::create_sandbox_config(const std::string &name,
134
+ const SandboxConfig &config) {
135
+ sandbox_configs_[name] = config;
136
+ return save_sandbox_config();
137
+ }
138
+
139
+ bool SandboxService::remove_sandbox_config(const std::string &name) {
140
+ // Don't allow removal of built-in configs
141
+ if (name == "default" || name == "python" || name == "nodejs" ||
142
+ name == "shell") {
143
+ return false;
144
+ }
145
+
146
+ auto it = sandbox_configs_.find(name);
147
+ if (it == sandbox_configs_.end()) {
148
+ return false;
149
+ }
150
+
151
+ sandbox_configs_.erase(it);
152
+ return save_sandbox_config();
153
+ }
154
+
155
+ std::vector<std::string> SandboxService::list_sandbox_configs() {
156
+ std::vector<std::string> config_names;
157
+ for (const auto &[name, config] : sandbox_configs_) {
158
+ config_names.push_back(name);
159
+ }
160
+ return config_names;
161
+ }
162
+
163
+ SandboxConfig SandboxService::get_sandbox_config(const std::string &name) {
164
+ auto it = sandbox_configs_.find(name);
165
+ if (it != sandbox_configs_.end()) {
166
+ return it->second;
167
+ }
168
+ return SandboxConfig{}; // Return empty config if not found
169
+ }
170
+
171
+ SandboxResult SandboxService::execute_command(const std::string &command,
172
+ const std::string &sandbox_name,
173
+ const std::string &working_dir) {
174
+ SandboxResult result;
175
+
176
+ if (!is_docker_available()) {
177
+ result.error_message = "Docker is not available";
178
+ result.exit_code = -1;
179
+ return result;
180
+ }
181
+
182
+ auto it = sandbox_configs_.find(sandbox_name);
183
+ if (it == sandbox_configs_.end()) {
184
+ result.error_message = "Sandbox configuration not found: " + sandbox_name;
185
+ result.exit_code = -1;
186
+ return result;
187
+ }
188
+
189
+ const SandboxConfig &config = it->second;
190
+
191
+ // Security check
192
+ if (!is_command_safe(command)) {
193
+ result.error_message = "Command failed security validation";
194
+ result.exit_code = -1;
195
+ return result;
196
+ }
197
+
198
+ // Build docker command
199
+ std::string container_name = generate_container_name();
200
+ std::stringstream docker_cmd;
201
+
202
+ docker_cmd << "docker run --rm";
203
+ docker_cmd << " --name " << container_name;
204
+ docker_cmd << " --memory=" << config.memory_limit_mb << "m";
205
+ docker_cmd << " --cpus=" << (config.cpu_limit_percent / 100.0);
206
+
207
+ if (!config.network_access) {
208
+ docker_cmd << " --network=none";
209
+ }
210
+
211
+ // Set working directory
212
+ std::string work_dir =
213
+ working_dir.empty() ? config.working_directory : working_dir;
214
+ docker_cmd << " --workdir=" << work_dir;
215
+
216
+ // Add environment variables
217
+ for (const auto &[key, value] : config.environment_vars) {
218
+ docker_cmd << " -e " << key << "=" << escape_shell_arg(value);
219
+ }
220
+
221
+ // Add volumes (be careful with security)
222
+ for (const auto &volume : config.volumes) {
223
+ docker_cmd << " -v " << volume;
224
+ }
225
+
226
+ docker_cmd << " " << config.image;
227
+ docker_cmd << " sh -c " << escape_shell_arg(command);
228
+
229
+ // Execute with timeout
230
+ auto start_time = std::chrono::high_resolution_clock::now();
231
+
232
+ std::string full_command = docker_cmd.str() + " 2>&1";
233
+ FILE *pipe = Utils::Platform::open_process(full_command, "r");
234
+
235
+ if (!pipe) {
236
+ result.error_message = "Failed to execute docker command";
237
+ result.exit_code = -1;
238
+ return result;
239
+ }
240
+
241
+ std::stringstream output;
242
+ char buffer[128];
243
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
244
+ output << buffer;
245
+ }
246
+
247
+ result.exit_code = Utils::Platform::close_process(pipe);
248
+ result.stdout_output = output.str();
249
+
250
+ auto end_time = std::chrono::high_resolution_clock::now();
251
+ auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
252
+ end_time - start_time);
253
+ result.execution_time_seconds = duration.count() / 1000.0;
254
+
255
+ // Check for timeout
256
+ if (result.execution_time_seconds > config.timeout_seconds) {
257
+ result.timed_out = true;
258
+ result.error_message = "Command execution timed out";
259
+ }
260
+
261
+ return result;
262
+ }
263
+
264
+ SandboxResult SandboxService::execute_script(const std::string &script_content,
265
+ const std::string &script_type,
266
+ const std::string &sandbox_name) {
267
+ SandboxResult result;
268
+
269
+ // Create temporary script file
270
+ std::string script_path = "data/sandbox/temp_script." + script_type;
271
+ std::ofstream script_file(script_path);
272
+ script_file << script_content;
273
+ script_file.close();
274
+
275
+ // Execute based on script type
276
+ std::string command;
277
+ if (script_type == "py" || script_type == "python") {
278
+ command = "python " + script_path;
279
+ } else if (script_type == "js" || script_type == "javascript") {
280
+ command = "node " + script_path;
281
+ } else if (script_type == "sh" || script_type == "bash") {
282
+ command = "sh " + script_path;
283
+ } else {
284
+ result.error_message = "Unsupported script type: " + script_type;
285
+ result.exit_code = -1;
286
+ return result;
287
+ }
288
+
289
+ result = execute_command(command, sandbox_name);
290
+
291
+ // Clean up temporary file
292
+ std::filesystem::remove(script_path);
293
+
294
+ return result;
295
+ }
296
+
297
+ SandboxResult SandboxService::execute_file(const std::string &file_path,
298
+ const std::string &sandbox_name) {
299
+ SandboxResult result;
300
+
301
+ if (!std::filesystem::exists(file_path)) {
302
+ result.error_message = "File not found: " + file_path;
303
+ result.exit_code = -1;
304
+ return result;
305
+ }
306
+
307
+ // Determine execution command based on file extension
308
+ std::string extension = std::filesystem::path(file_path).extension().string();
309
+ std::string command;
310
+
311
+ if (extension == ".py") {
312
+ command = "python " + file_path;
313
+ } else if (extension == ".js") {
314
+ command = "node " + file_path;
315
+ } else if (extension == ".sh") {
316
+ command = "sh " + file_path;
317
+ } else {
318
+ command = file_path; // Try to execute directly
319
+ }
320
+
321
+ return execute_command(command, sandbox_name);
322
+ }
323
+
324
+ bool SandboxService::copy_file_to_sandbox(const std::string &host_path,
325
+ const std::string &container_path,
326
+ const std::string &container_name) {
327
+ std::string command = "docker cp " + escape_shell_arg(host_path) + " " +
328
+ container_name + ":" + escape_shell_arg(container_path);
329
+ FILE *pipe = Utils::Platform::open_process(command.c_str(), "r");
330
+ if (!pipe)
331
+ return false;
332
+
333
+ // Read and discard output
334
+ char buffer[128];
335
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
336
+ }
337
+
338
+ int status = Utils::Platform::close_process(pipe);
339
+ #ifdef _WIN32
340
+ return status == 0; // On Windows, pclose returns the exit status directly
341
+ #else
342
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
343
+ #endif
344
+ }
345
+
346
+ bool SandboxService::copy_file_from_sandbox(const std::string &container_path,
347
+ const std::string &host_path,
348
+ const std::string &container_name) {
349
+ std::string command = "docker cp " + container_name + ":" +
350
+ escape_shell_arg(container_path) + " " +
351
+ escape_shell_arg(host_path);
352
+ FILE *pipe = Utils::Platform::open_process(command.c_str(), "r");
353
+ if (!pipe)
354
+ return false;
355
+
356
+ // Read and discard output
357
+ char buffer[128];
358
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
359
+ }
360
+
361
+ int status = Utils::Platform::close_process(pipe);
362
+ #ifdef _WIN32
363
+ return status == 0; // On Windows, pclose returns the exit status directly
364
+ #else
365
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
366
+ #endif
367
+ }
368
+
369
+ std::vector<std::string> SandboxService::list_active_containers() {
370
+ std::vector<std::string> containers;
371
+
372
+ FILE *pipe = Utils::Platform::open_process(
373
+ "docker ps --filter name=cursor_sandbox_ --format '{{.Names}}'", "r");
374
+ if (pipe) {
375
+ char buffer[256];
376
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
377
+ std::string name(buffer);
378
+ name.erase(name.find_last_not_of(" \n\r\t") + 1);
379
+ if (!name.empty()) {
380
+ containers.push_back(name);
381
+ }
382
+ }
383
+ Utils::Platform::close_process(pipe);
384
+ }
385
+
386
+ return containers;
387
+ }
388
+
389
+ bool SandboxService::stop_container(const std::string &container_name) {
390
+ std::string command = "docker stop " + escape_shell_arg(container_name);
391
+ FILE *pipe = Utils::Platform::open_process(command.c_str(), "r");
392
+ if (!pipe)
393
+ return false;
394
+
395
+ // Read and discard output
396
+ char buffer[128];
397
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
398
+ }
399
+
400
+ int status = Utils::Platform::close_process(pipe);
401
+ #ifdef _WIN32
402
+ return status == 0; // On Windows, pclose returns the exit status directly
403
+ #else
404
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
405
+ #endif
406
+ }
407
+
408
+ bool SandboxService::remove_container(const std::string &container_name) {
409
+ std::string command = "docker rm -f " + escape_shell_arg(container_name);
410
+ FILE *pipe = Utils::Platform::open_process(command.c_str(), "r");
411
+ if (!pipe)
412
+ return false;
413
+
414
+ // Read and discard output
415
+ char buffer[128];
416
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
417
+ }
418
+
419
+ int status = Utils::Platform::close_process(pipe);
420
+ #ifdef _WIN32
421
+ return status == 0; // On Windows, pclose returns the exit status directly
422
+ #else
423
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
424
+ #endif
425
+ }
426
+
427
+ void SandboxService::cleanup_old_containers() {
428
+ std::string command =
429
+ "docker container prune -f --filter label=cursor_sandbox";
430
+ FILE *pipe = Utils::Platform::open_process(command.c_str(), "r");
431
+ if (pipe) {
432
+ // Read and discard output
433
+ char buffer[128];
434
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
435
+ }
436
+ Utils::Platform::close_process(pipe);
437
+ }
438
+ }
439
+
440
+ bool SandboxService::is_command_safe(const std::string &command) {
441
+ // List of dangerous commands/patterns
442
+ std::vector<std::string> dangerous_patterns = {
443
+ "rm -rf", "format", "del /f", "sudo", "su -", "chmod 777",
444
+ "wget", "curl", "nc ", "netcat", "telnet", "ssh",
445
+ "scp", "iptables", "ufw", "firewall", "systemctl", "service",
446
+ "mount", "umount", "fdisk", "mkfs", "dd if=", "dd of=",
447
+ ">/dev/", "< /dev/", "| /dev/", "& /dev/", "$(", "`",
448
+ "eval", "exec", "source", ".", "bash -c", "sh -c",
449
+ "python -c", "perl -e", "ruby -e"};
450
+
451
+ std::string lower_command = command;
452
+ std::transform(lower_command.begin(), lower_command.end(),
453
+ lower_command.begin(), ::tolower);
454
+
455
+ for (const auto &pattern : dangerous_patterns) {
456
+ if (lower_command.find(pattern) != std::string::npos) {
457
+ return false;
458
+ }
459
+ }
460
+
461
+ return true;
462
+ }
463
+
464
+ bool SandboxService::validate_sandbox_config(const SandboxConfig &config,
465
+ std::string &error_message) {
466
+ if (config.name.empty()) {
467
+ error_message = "Sandbox name cannot be empty";
468
+ return false;
469
+ }
470
+
471
+ if (config.image.empty()) {
472
+ error_message = "Docker image cannot be empty";
473
+ return false;
474
+ }
475
+
476
+ if (config.memory_limit_mb < 64 || config.memory_limit_mb > 8192) {
477
+ error_message = "Memory limit must be between 64MB and 8192MB";
478
+ return false;
479
+ }
480
+
481
+ if (config.cpu_limit_percent < 1 || config.cpu_limit_percent > 100) {
482
+ error_message = "CPU limit must be between 1% and 100%";
483
+ return false;
484
+ }
485
+
486
+ if (config.timeout_seconds < 1 || config.timeout_seconds > 3600) {
487
+ error_message = "Timeout must be between 1 and 3600 seconds";
488
+ return false;
489
+ }
490
+
491
+ return true;
492
+ }
493
+
494
+ std::vector<std::string>
495
+ SandboxService::get_security_warnings(const std::string &command) {
496
+ std::vector<std::string> warnings;
497
+
498
+ if (command.find("sudo") != std::string::npos) {
499
+ warnings.push_back("Command contains 'sudo' - elevated privileges");
500
+ }
501
+
502
+ if (command.find("rm") != std::string::npos) {
503
+ warnings.push_back("Command contains 'rm' - file deletion");
504
+ }
505
+
506
+ if (command.find("wget") != std::string::npos ||
507
+ command.find("curl") != std::string::npos) {
508
+ warnings.push_back("Command contains network access tools");
509
+ }
510
+
511
+ return warnings;
512
+ }
513
+
514
+ bool SandboxService::save_sandbox_config() {
515
+ try {
516
+ ensure_sandbox_directory();
517
+
518
+ nlohmann::json config;
519
+ config["sandboxes"] = nlohmann::json::object();
520
+
521
+ for (const auto &[name, sandbox] : sandbox_configs_) {
522
+ nlohmann::json sandbox_json;
523
+ sandbox_json["name"] = sandbox.name;
524
+ sandbox_json["image"] = sandbox.image;
525
+ sandbox_json["volumes"] = sandbox.volumes;
526
+ sandbox_json["environment_vars"] = sandbox.environment_vars;
527
+ sandbox_json["allowed_commands"] = sandbox.allowed_commands;
528
+ sandbox_json["memory_limit_mb"] = sandbox.memory_limit_mb;
529
+ sandbox_json["cpu_limit_percent"] = sandbox.cpu_limit_percent;
530
+ sandbox_json["timeout_seconds"] = sandbox.timeout_seconds;
531
+ sandbox_json["network_access"] = sandbox.network_access;
532
+ sandbox_json["working_directory"] = sandbox.working_directory;
533
+
534
+ config["sandboxes"][name] = sandbox_json;
535
+ }
536
+
537
+ std::ofstream file(get_sandbox_config_path());
538
+ file << config.dump(2);
539
+
540
+ return true;
541
+
542
+ } catch (const std::exception &e) {
543
+ std::cerr << "Failed to save sandbox config: " << e.what() << std::endl;
544
+ return false;
545
+ }
546
+ }
547
+
548
+ bool SandboxService::load_sandbox_config() {
549
+ try {
550
+ std::string config_path = get_sandbox_config_path();
551
+ if (!std::filesystem::exists(config_path)) {
552
+ return true; // Use defaults
553
+ }
554
+
555
+ std::ifstream file(config_path);
556
+ nlohmann::json config;
557
+ file >> config;
558
+
559
+ if (config.contains("sandboxes")) {
560
+ for (const auto &[name, sandbox_json] : config["sandboxes"].items()) {
561
+ // Skip built-in configs
562
+ if (name == "default" || name == "python" || name == "nodejs" ||
563
+ name == "shell") {
564
+ continue;
565
+ }
566
+
567
+ SandboxConfig sandbox;
568
+ sandbox.name = sandbox_json.value("name", name);
569
+ sandbox.image = sandbox_json.value("image", "ubuntu:22.04");
570
+ sandbox.memory_limit_mb = sandbox_json.value("memory_limit_mb", 512);
571
+ sandbox.cpu_limit_percent = sandbox_json.value("cpu_limit_percent", 50);
572
+ sandbox.timeout_seconds = sandbox_json.value("timeout_seconds", 300);
573
+ sandbox.network_access = sandbox_json.value("network_access", false);
574
+ sandbox.working_directory =
575
+ sandbox_json.value("working_directory", "/workspace");
576
+
577
+ if (sandbox_json.contains("volumes")) {
578
+ sandbox.volumes =
579
+ sandbox_json["volumes"].get<std::vector<std::string>>();
580
+ }
581
+
582
+ if (sandbox_json.contains("environment_vars")) {
583
+ sandbox.environment_vars =
584
+ sandbox_json["environment_vars"]
585
+ .get<std::map<std::string, std::string>>();
586
+ }
587
+
588
+ if (sandbox_json.contains("allowed_commands")) {
589
+ sandbox.allowed_commands =
590
+ sandbox_json["allowed_commands"].get<std::vector<std::string>>();
591
+ }
592
+
593
+ sandbox_configs_[name] = sandbox;
594
+ }
595
+ }
596
+
597
+ return true;
598
+
599
+ } catch (const std::exception &e) {
600
+ std::cerr << "Failed to load sandbox config: " << e.what() << std::endl;
601
+ return false;
602
+ }
603
+ }
604
+
605
+ bool SandboxService::update_sandbox_limits(const std::string &name,
606
+ size_t memory_mb,
607
+ size_t cpu_percent) {
608
+ auto it = sandbox_configs_.find(name);
609
+ if (it == sandbox_configs_.end()) {
610
+ return false;
611
+ }
612
+
613
+ it->second.memory_limit_mb = memory_mb;
614
+ it->second.cpu_limit_percent = cpu_percent;
615
+
616
+ return save_sandbox_config();
617
+ }
618
+
619
+ bool SandboxService::check_docker_installation() {
620
+ return is_docker_available();
621
+ }
622
+
623
+ std::string SandboxService::get_docker_version() {
624
+ FILE *pipe = Utils::Platform::open_process(
625
+ "docker --version" + Utils::Platform::get_shell_redirect(), "r");
626
+ if (!pipe)
627
+ return "Not available";
628
+
629
+ char buffer[256];
630
+ std::string version;
631
+ if (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
632
+ version = buffer;
633
+ version.erase(version.find_last_not_of(" \n\r\t") + 1);
634
+ }
635
+ Utils::Platform::close_process(pipe);
636
+
637
+ return version.empty() ? "Not available" : version;
638
+ }
639
+
640
+ bool SandboxService::pull_docker_image(const std::string &image) {
641
+ std::string command = "docker pull " + escape_shell_arg(image);
642
+ FILE *pipe = Utils::Platform::open_process(command.c_str(), "r");
643
+ if (!pipe)
644
+ return false;
645
+
646
+ // Read and discard output
647
+ char buffer[128];
648
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
649
+ }
650
+
651
+ int status = Utils::Platform::close_process(pipe);
652
+ #ifdef _WIN32
653
+ return status == 0; // On Windows, pclose returns the exit status directly
654
+ #else
655
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
656
+ #endif
657
+ }
658
+
659
+ std::vector<std::string> SandboxService::list_available_images() {
660
+ std::vector<std::string> images;
661
+
662
+ FILE *pipe = Utils::Platform::open_process(
663
+ "docker images --format '{{.Repository}}:{{.Tag}}'", "r");
664
+ if (pipe) {
665
+ char buffer[256];
666
+ while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
667
+ std::string image(buffer);
668
+ image.erase(image.find_last_not_of(" \n\r\t") + 1);
669
+ if (!image.empty()) {
670
+ images.push_back(image);
671
+ }
672
+ }
673
+ Utils::Platform::close_process(pipe);
674
+ }
675
+
676
+ return images;
677
+ }
678
+ } // namespace Services