@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,307 @@
1
+ #include "utils/ui.h"
2
+ #include <array>
3
+ #include <chrono>
4
+ #include <iostream>
5
+ #include <string>
6
+ #include <thread>
7
+
8
+ namespace Utils {
9
+
10
+ // ===== Color Codes =====
11
+ namespace Color {
12
+ // Exported color constants
13
+ CURSOR_API const std::string RESET = "\033[0m";
14
+ CURSOR_API const std::string GREEN = "\033[32m";
15
+ CURSOR_API const std::string YELLOW = "\033[33m";
16
+ CURSOR_API const std::string RED = "\033[31m";
17
+ CURSOR_API const std::string CYAN = "\033[36m";
18
+ CURSOR_API const std::string BOLD = "\033[1m";
19
+ CURSOR_API const std::string DIM = "\033[2m";
20
+ CURSOR_API const std::string PINK = "\033[38;2;255;105;180m";
21
+ } // namespace Color
22
+
23
+ // ===== Logo =====
24
+ void UI::print_logo() {
25
+ std::cout << Color::PINK
26
+ << " ▄▄▄▄▄▄▄▄▄▄▄\n"
27
+ << " █ █\n"
28
+ << " █ ▄▄▄▄▄▄ █\n"
29
+ << " █ █ █ █\n"
30
+ << " █ █ ██ █ █\n"
31
+ << " █ █ █ █ █ █\n"
32
+ << " █ █ █ █ █ █\n"
33
+ << " █ █ ██ █ █\n"
34
+ << " █ █ █ █\n"
35
+ << " █ ▀▀▀▀▀▀ █\n"
36
+ << " █ █\n"
37
+ << " ▀▀▀▀▀▀▀▀▀\n"
38
+ << Color::RESET
39
+ << Color::BOLD << " CURSOR\n"
40
+ << Color::RESET;
41
+
42
+ std::cout << Color::DIM << "Welcome to Cursor - Choose your mode below.\n"
43
+ << Color::RESET;
44
+
45
+ print_divider();
46
+ }
47
+
48
+ // ===== Help Menu =====
49
+ void UI::print_help() {
50
+
51
+ std::cout << Color::BOLD << "Commands\n" << Color::RESET;
52
+ print_divider();
53
+
54
+ // File Operations
55
+ std::cout << Color::GREEN << "Files\n" << Color::RESET;
56
+ std::cout << Color::CYAN << " @file <path>" << Color::RESET
57
+ << " - Inject file\n";
58
+ std::cout << Color::CYAN << " @directory <path>" << Color::RESET
59
+ << " - Inject directory\n";
60
+ std::cout << Color::CYAN << " read:<file>" << Color::RESET
61
+ << " - Read file\n";
62
+ std::cout << Color::CYAN << " read:<file>:<start>:<count>" << Color::RESET
63
+ << " - Read range\n";
64
+ std::cout << Color::CYAN << " write:<file> <content>" << Color::RESET
65
+ << " - Write file\n";
66
+ std::cout << Color::CYAN << " replace:<file>:<old>:<new>" << Color::RESET
67
+ << " - Replace text\n";
68
+ std::cout << Color::CYAN << " grep:<pattern>[:dir[:filter]]" << Color::RESET
69
+ << " - Search text\n";
70
+
71
+ // Sessions
72
+ std::cout << "\n" << Color::GREEN << "Sessions\n" << Color::RESET;
73
+ std::cout << Color::CYAN << " /save <name> [tags]" << Color::RESET
74
+ << " - Save session\n";
75
+ std::cout << Color::CYAN << " /resume <name>" << Color::RESET
76
+ << " - Resume session\n";
77
+ std::cout << Color::CYAN << " /sessions" << Color::RESET
78
+ << " - List sessions\n";
79
+ std::cout << Color::CYAN << " /compress" << Color::RESET
80
+ << " - Compress context\n";
81
+ std::cout << Color::CYAN << " remember:<fact>" << Color::RESET
82
+ << " - Remember fact\n";
83
+ std::cout << Color::CYAN << " memory" << Color::RESET
84
+ << " - Show memory\n";
85
+ std::cout << Color::CYAN << " clear" << Color::RESET
86
+ << " - Clear session\n";
87
+ std::cout << Color::CYAN << " forget" << Color::RESET
88
+ << " - Clear memory\n";
89
+
90
+ // Web
91
+ std::cout << "\n" << Color::GREEN << "Web\n" << Color::RESET;
92
+ std::cout << Color::CYAN << " /fetch <url> [format]" << Color::RESET
93
+ << " - Fetch content\n";
94
+ std::cout << Color::CYAN << " search:<query>" << Color::RESET
95
+ << " - Search web\n";
96
+ std::cout << Color::CYAN << " /mcp servers" << Color::RESET
97
+ << " - List servers\n";
98
+ std::cout << Color::CYAN << " /mcp resources <server>" << Color::RESET
99
+ << " - List resources\n";
100
+ std::cout << Color::CYAN << " /mcp tools <server>" << Color::RESET
101
+ << " - List tools\n";
102
+
103
+ // Checkpoints
104
+ std::cout << "\n" << Color::GREEN << "Checkpoints\n" << Color::RESET;
105
+ std::cout << Color::CYAN << " /checkpoint <name>" << Color::RESET
106
+ << " - Create checkpoint\n";
107
+ std::cout << Color::CYAN << " /restore <name>" << Color::RESET
108
+ << " - Restore checkpoint\n";
109
+ std::cout << Color::CYAN << " /checkpoints" << Color::RESET
110
+ << " - List checkpoints\n";
111
+
112
+ // Themes
113
+ std::cout << "\n" << Color::GREEN << "Themes\n" << Color::RESET;
114
+ std::cout << Color::CYAN << " /theme list" << Color::RESET
115
+ << " - List themes\n";
116
+ std::cout << Color::CYAN << " /theme set <name>" << Color::RESET
117
+ << " - Set theme\n";
118
+ std::cout << Color::CYAN << " /theme preview <name>" << Color::RESET
119
+ << " - Preview theme\n";
120
+
121
+ // Security
122
+ std::cout << "\n" << Color::GREEN << "Security\n" << Color::RESET;
123
+ std::cout << Color::CYAN << " /auth providers" << Color::RESET
124
+ << " - List providers\n";
125
+ std::cout << Color::CYAN << " /auth set <provider>" << Color::RESET
126
+ << " - Set provider\n";
127
+ std::cout << Color::CYAN << " /auth key <provider> <key>" << Color::RESET
128
+ << " - Set key\n";
129
+ std::cout << Color::CYAN << " /sandbox run <command>" << Color::RESET
130
+ << " - Run sandboxed\n";
131
+ std::cout << Color::CYAN << " /sandbox status" << Color::RESET
132
+ << " - Check status\n";
133
+
134
+ // Errors
135
+ std::cout << "\n" << Color::GREEN << "Errors\n" << Color::RESET;
136
+ std::cout << Color::CYAN << " /error report" << Color::RESET
137
+ << " - View errors\n";
138
+ std::cout << Color::CYAN << " /error recent <count>" << Color::RESET
139
+ << " - Show recent\n";
140
+ std::cout << Color::CYAN << " /error export <file>" << Color::RESET
141
+ << " - Export log\n";
142
+
143
+ // System
144
+ std::cout << "\n" << Color::GREEN << "System\n" << Color::RESET;
145
+ std::cout << Color::CYAN << " cmd:<command>" << Color::RESET
146
+ << " - Execute shell command\n";
147
+ std::cout << Color::CYAN << " /update" << Color::RESET
148
+ << " - Check for updates\n";
149
+ std::cout << Color::CYAN << " /tools" << Color::RESET
150
+ << " - Show tools\n";
151
+ std::cout << Color::CYAN << " !" << Color::RESET
152
+ << " - Toggle shell\n";
153
+ std::cout << Color::CYAN << " help" << Color::RESET
154
+ << " - Show help\n";
155
+ std::cout << Color::CYAN << " quit" << Color::RESET
156
+ << " - Exit\n";
157
+
158
+ std::cout << "\n" << Color::BOLD << "Notes\n" << Color::RESET;
159
+ std::cout << Color::DIM << "Set theme with /theme set dark\n" << Color::RESET;
160
+ std::cout << Color::DIM << "Configure auth with /auth key\n" << Color::RESET;
161
+ std::cout << Color::DIM << "Create checkpoints before changes\n"
162
+ << Color::RESET;
163
+ std::cout << Color::DIM << "Use /sandbox for safe execution\n"
164
+ << Color::RESET;
165
+ print_divider();
166
+ }
167
+
168
+ // ===== Status =====
169
+ void UI::print_enterprise_status() {
170
+ std::cout << Color::BOLD << "Status\n" << Color::RESET;
171
+ print_divider();
172
+
173
+ std::cout << Color::GREEN << "Files (4/4)\n" << Color::RESET;
174
+ std::cout << Color::DIM << " File injection\n" << Color::RESET;
175
+ std::cout << Color::DIM << " Multi-file operations\n" << Color::RESET;
176
+ std::cout << Color::DIM << " Context hierarchy\n" << Color::RESET;
177
+ std::cout << Color::DIM << " Shell integration\n" << Color::RESET;
178
+
179
+ std::cout << "\n" << Color::GREEN << "Sessions (4/4)\n" << Color::RESET;
180
+ std::cout << Color::DIM << " Session management\n" << Color::RESET;
181
+ std::cout << Color::DIM << " Tool registry\n" << Color::RESET;
182
+ std::cout << Color::DIM << " Configuration\n" << Color::RESET;
183
+ std::cout << Color::DIM << " Context compression\n" << Color::RESET;
184
+
185
+ std::cout << "\n" << Color::GREEN << "Extensions (4/4)\n" << Color::RESET;
186
+ std::cout << Color::DIM << " MCP servers\n" << Color::RESET;
187
+ std::cout << Color::DIM << " Checkpoints\n" << Color::RESET;
188
+ std::cout << Color::DIM << " Web fetch\n" << Color::RESET;
189
+ std::cout << Color::DIM << " File filtering\n" << Color::RESET;
190
+
191
+ std::cout << "\n" << Color::GREEN << "Security (4/4)\n" << Color::RESET;
192
+ std::cout << Color::DIM << " Themes\n" << Color::RESET;
193
+ std::cout << Color::DIM << " Authentication\n" << Color::RESET;
194
+ std::cout << Color::DIM << " Sandboxing\n" << Color::RESET;
195
+ std::cout << Color::DIM << " Error handling\n" << Color::RESET;
196
+
197
+ std::cout << "\n"
198
+ << Color::BOLD << Color::GREEN << "16 features active\n"
199
+ << Color::RESET;
200
+ print_divider();
201
+ }
202
+
203
+ // ===== Spinner =====
204
+ void UI::spinner(const std::string &message, int duration_ms) {
205
+ const std::array<std::string_view, 4> frames = {"/", "-", "\\", "|"};
206
+ const size_t num_frames = frames.size();
207
+ size_t frame = 0;
208
+ auto start = std::chrono::steady_clock::now();
209
+
210
+ std::cout << Color::CYAN << message << " " << Color::RESET;
211
+ while (std::chrono::steady_clock::now() - start <
212
+ std::chrono::milliseconds(duration_ms)) {
213
+ std::cout << "\b" << frames[frame] << std::flush;
214
+ frame = (frame + 1) % num_frames;
215
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
216
+ }
217
+ std::cout << "\b" << " " << "\n";
218
+ }
219
+
220
+ // ===== Threaded Spinner =====
221
+ void UI::spinner(std::atomic<bool> &done) {
222
+ const char frames[] = {'|', '/', '-', '\\'};
223
+ int frame = 0;
224
+
225
+ while (!done) {
226
+ std::cout << "\r" << frames[frame % 4] << std::flush;
227
+ frame++;
228
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
229
+ }
230
+ std::cout << "\r \r" << std::flush;
231
+ }
232
+
233
+ // ===== Status Messages =====
234
+ void UI::print_success(const std::string &message) {
235
+ std::cout << Color::GREEN << "[OK] " << Color::RESET << message << "\n";
236
+ }
237
+
238
+ void UI::print_error(const std::string &message) {
239
+ std::cout << Color::RED << "[ERROR] " << Color::RESET << message << "\n";
240
+ }
241
+
242
+ void UI::print_warning(const std::string &message) {
243
+ std::cout << Color::YELLOW << "[WARN] " << Color::RESET << message << "\n";
244
+ }
245
+
246
+ void UI::print_info(const std::string &message) {
247
+ std::cout << Color::CYAN << "[INFO] " << Color::RESET << message << "\n";
248
+ }
249
+
250
+ // ===== Divider =====
251
+ void UI::print_divider() {
252
+ std::cout << Color::DIM << "----------------------------------------\n"
253
+ << Color::RESET;
254
+ }
255
+
256
+ // ===== Quick Help Interface =====
257
+ void UI::print_quick_help() {
258
+ std::cout << Color::DIM << "Quick Commands: " << Color::RESET;
259
+ std::cout << Color::CYAN << "help" << Color::RESET << " | ";
260
+
261
+ std::cout << Color::CYAN << "search:query" << Color::RESET << " | ";
262
+ std::cout << Color::CYAN << "cmd:command" << Color::RESET << " | ";
263
+ std::cout << Color::CYAN << "read:file" << Color::RESET << " | ";
264
+ std::cout << Color::CYAN << "quit" << Color::RESET << "\n";
265
+
266
+ std::cout << Color::CYAN << "version" << Color::RESET << " | ";
267
+ std::cout << Color::CYAN << "update" << Color::RESET << " | ";
268
+ std::cout << Color::CYAN << "search:query" << Color::RESET << " | ";
269
+ std::cout << Color::CYAN << "cmd:command" << Color::RESET << " | ";
270
+ std::cout << Color::CYAN << "read:file" << Color::RESET << " | ";
271
+ std::cout << Color::CYAN << "write:file text" << Color::RESET << " | ";
272
+ std::cout << Color::CYAN << "exit" << Color::RESET << "\n";
273
+
274
+ print_divider();
275
+ }
276
+
277
+ // ===== System Info =====
278
+ void UI::print_system_info(const std::string &mode, const std::string &model) {
279
+ std::cout << Color::DIM << "System: " << Color::RESET;
280
+ std::cout << Color::GREEN << "Mode=" << mode << Color::RESET << " | ";
281
+ std::cout << Color::GREEN << "Model=" << model << Color::RESET << " | ";
282
+ std::cout << Color::GREEN << "Memory=Active" << Color::RESET << " | ";
283
+ std::cout << Color::GREEN << "Commands=Available" << Color::RESET << "\n";
284
+ }
285
+
286
+ // ===== Ready Interface =====
287
+ void UI::print_ready_interface(const std::string &mode,
288
+ const std::string &model) {
289
+ print_system_info(mode, model);
290
+ print_quick_help();
291
+
292
+ std::cout << "Ready - Type a command:\n";
293
+
294
+ std::cout << "Ready - Type a command or chat naturally:\n";
295
+ }
296
+
297
+ // ===== Prompted Input =====
298
+ std::string UI::prompt_user(const std::string &prompt_text) {
299
+ // Only print the prompt once with consistent formatting
300
+ std::cout << Color::BOLD << "[You] " << prompt_text << Color::RESET << " > ";
301
+
302
+ std::string input;
303
+ std::getline(std::cin, input);
304
+ return input;
305
+ }
306
+
307
+ } // namespace Utils
@@ -0,0 +1,306 @@
1
+ #include "utils/validation.h"
2
+ #include "services/file_service.h"
3
+ #include <algorithm>
4
+ #include <cctype>
5
+ #include <filesystem>
6
+ #include <regex>
7
+
8
+ namespace Utils {
9
+
10
+ ValidationResult
11
+ Validator::validate_file_path(const std::string &path,
12
+ const std::string &base_directory) {
13
+ ValidationResult result = {true, "", {}};
14
+
15
+ if (path.empty()) {
16
+ result.is_valid = false;
17
+ result.error_message = "File path cannot be empty";
18
+ return result;
19
+ }
20
+
21
+ // Check for dangerous path patterns
22
+ if (path.find("..") != std::string::npos) {
23
+ result.warnings.push_back("Path contains '..' which may be unsafe");
24
+ }
25
+
26
+ // Validate against base directory if provided
27
+ if (!base_directory.empty()) {
28
+ if (!Services::FileService::is_within_directory(path, base_directory)) {
29
+ result.is_valid = false;
30
+ result.error_message = "Path is outside the allowed base directory";
31
+ return result;
32
+ }
33
+ }
34
+
35
+ // Check path length
36
+ if (path.length() > 260) { // Windows MAX_PATH limit
37
+ result.warnings.push_back(
38
+ "Path is very long and may cause issues on some systems");
39
+ }
40
+
41
+ return result;
42
+ }
43
+
44
+ ValidationResult Validator::validate_file_exists(const std::string &path) {
45
+ ValidationResult result = {true, "", {}};
46
+
47
+ if (!Services::FileService::file_exists(path)) {
48
+ result.is_valid = false;
49
+ result.error_message = "File does not exist: " + path;
50
+ }
51
+
52
+ return result;
53
+ }
54
+
55
+ ValidationResult Validator::validate_file_writable(const std::string &path) {
56
+ ValidationResult result = {true, "", {}};
57
+
58
+ try {
59
+ std::filesystem::path file_path(path);
60
+ std::filesystem::path parent_path = file_path.parent_path();
61
+
62
+ // Check if parent directory exists and is writable
63
+ if (!parent_path.empty() && !std::filesystem::exists(parent_path)) {
64
+ // Try to create parent directories
65
+ try {
66
+ std::filesystem::create_directories(parent_path);
67
+ } catch (const std::exception &e) {
68
+ result.is_valid = false;
69
+ result.error_message =
70
+ "Cannot create parent directory: " + std::string(e.what());
71
+ return result;
72
+ }
73
+ }
74
+
75
+ // Check if file exists and is writable
76
+ if (std::filesystem::exists(path)) {
77
+ auto perms = std::filesystem::status(path).permissions();
78
+ if ((perms & std::filesystem::perms::owner_write) ==
79
+ std::filesystem::perms::none) {
80
+ result.warnings.push_back("File may not be writable");
81
+ }
82
+ }
83
+
84
+ } catch (const std::exception &e) {
85
+ result.is_valid = false;
86
+ result.error_message =
87
+ "Error checking file writability: " + std::string(e.what());
88
+ }
89
+
90
+ return result;
91
+ }
92
+
93
+ ValidationResult Validator::validate_non_empty(const std::string &text,
94
+ const std::string &field_name) {
95
+ ValidationResult result = {true, "", {}};
96
+
97
+ if (text.empty()) {
98
+ result.is_valid = false;
99
+ result.error_message = field_name + " cannot be empty";
100
+ } else if (text.find_first_not_of(" \t\n\r") == std::string::npos) {
101
+ result.is_valid = false;
102
+ result.error_message = field_name + " cannot be only whitespace";
103
+ }
104
+
105
+ return result;
106
+ }
107
+
108
+ ValidationResult Validator::validate_regex_pattern(const std::string &pattern) {
109
+ ValidationResult result = {true, "", {}};
110
+
111
+ try {
112
+ std::regex test_regex(pattern);
113
+ (void)test_regex; // Suppress unused variable warning - we only care about
114
+ // construction validity
115
+ } catch (const std::regex_error &e) {
116
+ result.is_valid = false;
117
+ result.error_message =
118
+ "Invalid regular expression: " + std::string(e.what());
119
+ }
120
+
121
+ return result;
122
+ }
123
+
124
+ ValidationResult Validator::validate_command_safe(const std::string &command) {
125
+ ValidationResult result = {true, "", {}};
126
+
127
+ // List of potentially dangerous commands
128
+ static const std::vector<std::string> dangerous_commands = {
129
+ "rm", "del", "format", "fdisk", "mkfs", "dd", "shutdown",
130
+ "reboot", "halt", "poweroff", "init", "kill", "killall", "pkill"};
131
+
132
+ std::string lower_command = command;
133
+ std::transform(lower_command.begin(), lower_command.end(),
134
+ lower_command.begin(), ::tolower);
135
+
136
+ for (const auto &dangerous : dangerous_commands) {
137
+ if (lower_command.find(dangerous) != std::string::npos) {
138
+ result.warnings.push_back(
139
+ "Command contains potentially dangerous operation: " + dangerous);
140
+ break;
141
+ }
142
+ }
143
+
144
+ // Check for shell injection patterns
145
+ if (command.find(";") != std::string::npos ||
146
+ command.find("&&") != std::string::npos ||
147
+ command.find("||") != std::string::npos ||
148
+ command.find("|") != std::string::npos) {
149
+ result.warnings.push_back(
150
+ "Command contains shell operators that may be unsafe");
151
+ }
152
+
153
+ return result;
154
+ }
155
+
156
+ ValidationResult Validator::validate_search_query(const std::string &query) {
157
+ ValidationResult result = validate_non_empty(query, "Search query");
158
+
159
+ if (result.is_valid) {
160
+ if (query.length() > 1000) {
161
+ result.warnings.push_back("Search query is very long");
162
+ }
163
+
164
+ // Check for potentially problematic characters
165
+ if (query.find_first_of("<>\"'&") != std::string::npos) {
166
+ result.warnings.push_back(
167
+ "Query contains special characters that may affect search results");
168
+ }
169
+ }
170
+
171
+ return result;
172
+ }
173
+
174
+ ValidationResult Validator::validate_line_range(int start_line, int line_count,
175
+ int total_lines) {
176
+ ValidationResult result = {true, "", {}};
177
+
178
+ if (start_line < 0) {
179
+ result.is_valid = false;
180
+ result.error_message = "Start line cannot be negative";
181
+ return result;
182
+ }
183
+
184
+ if (line_count < -1) {
185
+ result.is_valid = false;
186
+ result.error_message = "Line count must be -1 (all) or positive";
187
+ return result;
188
+ }
189
+
190
+ if (total_lines >= 0) {
191
+ if (start_line >= total_lines) {
192
+ result.is_valid = false;
193
+ result.error_message = "Start line is beyond end of file";
194
+ return result;
195
+ }
196
+
197
+ if (line_count > 0 && start_line + line_count > total_lines) {
198
+ result.warnings.push_back("Requested range extends beyond end of file");
199
+ }
200
+ }
201
+
202
+ return result;
203
+ }
204
+
205
+ ValidationResult Validator::validate_replacement_count(int expected,
206
+ int actual) {
207
+ ValidationResult result = {true, "", {}};
208
+
209
+ if (expected > 0 && expected != actual) {
210
+ result.is_valid = false;
211
+ result.error_message = "Expected " + std::to_string(expected) +
212
+ " replacements but found " + std::to_string(actual);
213
+ } else if (actual == 0) {
214
+ result.is_valid = false;
215
+ result.error_message = "No matches found for replacement";
216
+ }
217
+
218
+ return result;
219
+ }
220
+
221
+ ValidationResult
222
+ Validator::combine_results(const std::vector<ValidationResult> &results) {
223
+ ValidationResult combined = {true, "", {}};
224
+
225
+ for (const auto &result : results) {
226
+ if (!result.is_valid) {
227
+ combined.is_valid = false;
228
+ if (combined.error_message.empty()) {
229
+ combined.error_message = result.error_message;
230
+ } else {
231
+ combined.error_message += "; " + result.error_message;
232
+ }
233
+ }
234
+
235
+ // Combine warnings
236
+ combined.warnings.insert(combined.warnings.end(), result.warnings.begin(),
237
+ result.warnings.end());
238
+ }
239
+
240
+ return combined;
241
+ }
242
+
243
+ bool Validator::is_safe_path(const std::string &path) {
244
+ // Check for path traversal attempts
245
+ if (path.find("..") != std::string::npos)
246
+ return false;
247
+
248
+ // Check for absolute paths that might be dangerous
249
+ if (path.find("/etc/") == 0 || path.find("/sys/") == 0 ||
250
+ path.find("/proc/") == 0 || path.find("/dev/") == 0) {
251
+ return false;
252
+ }
253
+
254
+ // Check for Windows system paths
255
+ if (path.find("C:\\Windows\\") == 0 || path.find("C:\\System32\\") == 0) {
256
+ return false;
257
+ }
258
+
259
+ return true;
260
+ }
261
+
262
+ bool Validator::is_text_file_extension(const std::string &extension) {
263
+ const auto &text_extensions = get_text_extensions();
264
+
265
+ std::string lower_ext = extension;
266
+ std::transform(lower_ext.begin(), lower_ext.end(), lower_ext.begin(),
267
+ ::tolower);
268
+
269
+ return std::find(text_extensions.begin(), text_extensions.end(), lower_ext) !=
270
+ text_extensions.end();
271
+ }
272
+
273
+ const std::vector<std::string> &Validator::get_text_extensions() {
274
+ static const std::vector<std::string> text_extensions = {
275
+ ".txt", ".md", ".rst", ".log", ".cfg", ".conf",
276
+ ".ini", ".yaml", ".yml", ".json", ".xml", ".html",
277
+ ".htm", ".css", ".js", ".ts", ".jsx", ".tsx",
278
+ ".c", ".cpp", ".cc", ".cxx", ".h", ".hpp",
279
+ ".hxx", ".java", ".py", ".rb", ".go", ".rs",
280
+ ".php", ".pl", ".sh", ".bash", ".zsh", ".fish",
281
+ ".ps1", ".sql", ".r", ".m", ".swift", ".kt",
282
+ ".scala", ".clj", ".hs", ".ml", ".dockerfile", ".makefile",
283
+ ".cmake", ".gradle", ".maven", ".sbt", ".bat", ".csv",
284
+ ".tsv"};
285
+ return text_extensions;
286
+ }
287
+
288
+ std::string Validator::sanitize_input(const std::string &input) {
289
+ std::string sanitized = input;
290
+
291
+ // Remove null bytes
292
+ sanitized.erase(std::remove(sanitized.begin(), sanitized.end(), '\0'),
293
+ sanitized.end());
294
+
295
+ // Trim whitespace
296
+ size_t start = sanitized.find_first_not_of(" \t\n\r");
297
+ if (start == std::string::npos)
298
+ return "";
299
+
300
+ size_t end = sanitized.find_last_not_of(" \t\n\r");
301
+ sanitized = sanitized.substr(start, end - start + 1);
302
+
303
+ return sanitized;
304
+ }
305
+
306
+ } // namespace Utils