Skip to content

Commit 39e29aa

Browse files
committed
Fix OS Command Injection vulnerabilities
1 parent 5fcfb3b commit 39e29aa

4 files changed

Lines changed: 119 additions & 48 deletions

File tree

main.c

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
#include <stdlib.h>
2121
#include <string.h>
2222

23+
#ifdef _WIN32
24+
#include <process.h>
25+
#else
26+
#include <unistd.h>
27+
#include <sys/wait.h>
28+
#endif
29+
2330

2431
static void repl(VM *vm) {
2532
char line[1024];
@@ -309,20 +316,40 @@ static int dispatchPRM(int argc, const char* argv[]) {
309316

310317
if (strcmp(sub, "run") == 0) {
311318
printf("[PRM] Running project: %s v%s\n", pname, pversion);
312-
char command[1152];
313-
snprintf(command, sizeof(command), "proxpl \"%s\"", pentry);
314-
printf("[PRM] Executing: %s\n", command);
315-
int code = system(command);
319+
printf("[PRM] Executing: proxpl %s\n", pentry);
320+
int code = -1;
321+
#ifdef _WIN32
322+
code = _spawnlp(_P_WAIT, "proxpl", "proxpl", pentry, NULL);
323+
#else
324+
pid_t pid = fork();
325+
if (pid == 0) {
326+
execlp("proxpl", "proxpl", pentry, (char*)NULL);
327+
exit(1);
328+
} else if (pid > 0) {
329+
int status;
330+
waitpid(pid, &status, 0);
331+
if (WIFEXITED(status)) code = WEXITSTATUS(status);
332+
}
333+
#endif
316334
if (code != 0) printf("[PRM] Process exited with code %d\n", code);
317335

318336
} else if (strcmp(sub, "build") == 0) {
319337
int releaseMode = (argc >= 3 && strcmp(argv[2], "--release") == 0);
320338
printf("[PRM] Building project: %s v%s%s\n", pname, pversion, releaseMode ? " (release)" : "");
321339
printf("Compile-only mode not fully supported yet, running instead...\n");
322-
char command[1152];
323-
snprintf(command, sizeof(command), "proxpl \"%s\"", pentry);
324-
printf("[PRM] Executing: %s\n", command);
325-
system(command);
340+
printf("[PRM] Executing: proxpl %s\n", pentry);
341+
#ifdef _WIN32
342+
_spawnlp(_P_WAIT, "proxpl", "proxpl", pentry, NULL);
343+
#else
344+
pid_t pid = fork();
345+
if (pid == 0) {
346+
execlp("proxpl", "proxpl", pentry, (char*)NULL);
347+
exit(1);
348+
} else if (pid > 0) {
349+
int status;
350+
waitpid(pid, &status, 0);
351+
}
352+
#endif
326353

327354
} else if (strcmp(sub, "test") == 0) {
328355
printf("Running tests for %s...\n", pname);

src/main.c

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
#include <stdlib.h>
2424
#include <string.h>
2525

26+
#ifdef _WIN32
27+
#include <process.h>
28+
#else
29+
#include <unistd.h>
30+
#include <sys/wait.h>
31+
#endif
2632

2733
void registerStdLib(VM* vm);
2834

@@ -246,15 +252,6 @@ static void runFile(const char *path) {
246252
// PRM Command Dispatch
247253
// Returns 1 if handled as a PRM command, 0 to continue with normal dispatch
248254
// ============================================================
249-
static int isValidArg(const char* arg) {
250-
if (!arg) return 0;
251-
while (*arg) {
252-
if (strchr("&|;><`$\\", *arg)) return 0;
253-
arg++;
254-
}
255-
return 1;
256-
}
257-
258255
static int dispatchPRM(int argc, const char* argv[]) {
259256
// Check if invoked as "prm" / "prm.exe" / "prm.bat"
260257
const char* exe = argv[0];
@@ -364,22 +361,23 @@ static int dispatchPRM(int argc, const char* argv[]) {
364361

365362
if (strcmp(sub, "run") == 0) {
366363
printf("[PRM] Running project: %s v%s\n", pname, pversion);
367-
char command[1152];
368-
// Use argv[0] to re-invoke the same executable
369-
// On Windows, system() uses cmd.exe /c string
370-
// If the string starts with a quote, we need to wrap the WHOLE thing in quotes
364+
printf("[PRM] Executing: %s %s\n", argv[0], pentry);
365+
366+
int code = -1;
371367
#ifdef _WIN32
372-
snprintf(command, sizeof(command), "\"\"%s\" \"%s\"\"", argv[0], pentry);
368+
code = _spawnl(_P_WAIT, argv[0], argv[0], pentry, NULL);
373369
#else
374-
snprintf(command, sizeof(command), "\"%s\" \"%s\"", argv[0], pentry);
370+
pid_t pid = fork();
371+
if (pid == 0) {
372+
execl(argv[0], argv[0], pentry, (char*)NULL);
373+
exit(1);
374+
} else if (pid > 0) {
375+
int status;
376+
waitpid(pid, &status, 0);
377+
if (WIFEXITED(status)) code = WEXITSTATUS(status);
378+
}
375379
#endif
376380

377-
printf("[PRM] Executing: %s\n", command);
378-
if (!isValidArg(argv[0]) || !isValidArg(pentry)) {
379-
fprintf(stderr, "Error: Invalid characters in command arguments.\n");
380-
return 1;
381-
}
382-
int code = system(command);
383381
if (code != 0) printf("[PRM] Process exited with code %d\n", code);
384382

385383
} else if (strcmp(sub, "build") == 0) {
@@ -405,20 +403,23 @@ static int dispatchPRM(int argc, const char* argv[]) {
405403
int releaseMode = (argc >= 3 && strcmp(argv[2], "--release") == 0);
406404
printf("[PRM] Building project: %s v%s%s\n", pname, pversion, releaseMode ? " (release)" : "");
407405
printf("Compile-only mode not fully supported yet, running instead...\n");
408-
char command[1152];
406+
printf("[PRM] Executing: %s %s\n", argv[0], pentry);
409407

408+
int res = -1;
410409
#ifdef _WIN32
411-
snprintf(command, sizeof(command), "\"\"%s\" \"%s\"\"", argv[0], pentry);
410+
res = _spawnl(_P_WAIT, argv[0], argv[0], pentry, NULL);
412411
#else
413-
snprintf(command, sizeof(command), "\"%s\" \"%s\"", argv[0], pentry);
412+
pid_t pid = fork();
413+
if (pid == 0) {
414+
execl(argv[0], argv[0], pentry, (char*)NULL);
415+
exit(1);
416+
} else if (pid > 0) {
417+
int status;
418+
waitpid(pid, &status, 0);
419+
if (WIFEXITED(status)) res = WEXITSTATUS(status);
420+
}
414421
#endif
415422

416-
printf("[PRM] Executing: %s\n", command);
417-
if (!isValidArg(argv[0]) || !isValidArg(pentry)) {
418-
fprintf(stderr, "Error: Invalid characters in command arguments.\n");
419-
return 1;
420-
}
421-
int res = system(command);
422423
(void)res;
423424
}
424425

src/prm/builder.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
#include "scanner.h"
77
#include "parser.h"
88

9+
#ifdef _WIN32
10+
#include <process.h>
11+
#else
12+
#include <unistd.h>
13+
#include <sys/wait.h>
14+
#endif
15+
916
// Helper to read file content for parsing
1017
static char* read_file_prm(const char* path) {
1118
FILE* file = fopen(path, "rb");
@@ -47,8 +54,22 @@ static void invoke_compiler(const char* file, bool run) {
4754
return;
4855
}
4956

50-
printf("[PRM] Executing: %s\n", command);
51-
int code = system(command);
57+
printf("[PRM] Executing: %s \"%s\"\n", exe, file);
58+
59+
int code = -1;
60+
#ifdef _WIN32
61+
code = _spawnlp(_P_WAIT, exe, exe, file, NULL);
62+
#else
63+
pid_t pid = fork();
64+
if (pid == 0) {
65+
execlp(exe, exe, file, (char*)NULL);
66+
exit(1);
67+
} else if (pid > 0) {
68+
int status;
69+
waitpid(pid, &status, 0);
70+
if (WIFEXITED(status)) code = WEXITSTATUS(status);
71+
}
72+
#endif
5273

5374
if (code != 0) {
5475
printf("[PRM] Process exited with code %d\n", code);

src/prm/commands/cmd_core.c

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
#include <string.h>
44
#include "../prm.h"
55

6+
#ifdef _WIN32
7+
#include <process.h>
8+
#include <direct.h>
9+
#else
10+
#include <unistd.h>
11+
#include <sys/wait.h>
12+
#include <sys/stat.h>
13+
#endif
14+
615
// --- Core ---
716
static int isValidPrmArg(const char* arg) {
817
if (!arg) return 0;
@@ -119,9 +128,9 @@ void prm_install(const char* packageName) {
119128

120129
// Ensure prox_modules directory exists
121130
#ifdef _WIN32
122-
system("if not exist prox_modules mkdir prox_modules");
131+
_mkdir("prox_modules");
123132
#else
124-
system("mkdir -p prox_modules");
133+
mkdir("prox_modules", 0777);
125134
#endif
126135

127136
// Determine target folder name from package name
@@ -142,27 +151,40 @@ void prm_install(const char* packageName) {
142151
targetPath[len - 4] = '\0';
143152
}
144153

145-
// Construct git clone command
146-
char command[512];
154+
char packageUrl[512];
147155

148156
if (strstr(packageName, "://")) {
149157
// Full URL
150158
if (!isValidPrmArg(packageName)) {
151159
fprintf(stderr, "Error: Invalid characters in package URL.\n");
152160
return;
153161
}
154-
snprintf(command, sizeof(command), "git clone %s %s", packageName, targetPath);
162+
snprintf(packageUrl, sizeof(packageUrl), "%s", packageName);
155163
} else {
156164
// "User/Repo" format -> GitHub
157165
if (!isValidPrmArg(packageName)) {
158166
fprintf(stderr, "Error: Invalid characters in package name.\n");
159167
return;
160168
}
161-
snprintf(command, sizeof(command), "git clone https://github.com/%s.git %s", packageName, targetPath);
169+
snprintf(packageUrl, sizeof(packageUrl), "https://github.com/%s.git", packageName);
162170
}
163171

164-
printf("Running: %s\n", command);
165-
int result = system(command);
172+
printf("Running: git clone %s %s\n", packageUrl, targetPath);
173+
int result = -1;
174+
175+
#ifdef _WIN32
176+
result = _spawnlp(_P_WAIT, "git", "git", "clone", packageUrl, targetPath, NULL);
177+
#else
178+
pid_t pid = fork();
179+
if (pid == 0) {
180+
execlp("git", "git", "clone", packageUrl, targetPath, (char*)NULL);
181+
exit(1);
182+
} else if (pid > 0) {
183+
int status;
184+
waitpid(pid, &status, 0);
185+
if (WIFEXITED(status)) result = WEXITSTATUS(status);
186+
}
187+
#endif
166188

167189
if (result == 0) {
168190
printf("Successfully installed %s to %s.\n", packageName, targetPath);

0 commit comments

Comments
 (0)