Skip to content

CodeQL#19

Closed
Reboy20000 wants to merge 7 commits intomainfrom
Test
Closed

CodeQL#19
Reboy20000 wants to merge 7 commits intomainfrom
Test

Conversation

@Reboy20000
Copy link
Member

No description provided.


if (!write_ast_to_spc(argv[2], root)) {
fprintf(stderr, "Could not write: %s\n", argv[2]);
if (!write_spc(out_path, root)) {

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This argument to a file access function is derived from [user input (a command-line argument)](1) and then passed to write_spc(out_path), which calls open(__path).

Copilot Autofix

AI 8 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

Read SPC into memory
========================= */

FILE *f = fopen(path, "rb");

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression

This argument to a file access function is derived from [user input (a command-line argument)](1) and then passed to fopen(__filename).

Copilot Autofix

AI 8 days ago

In general, the fix is to avoid passing raw user input into fopen and instead (1) validate that the input is syntactically safe (no absolute paths, no .. components, etc.) and (2) ensure that the resolved path is contained within an allowed base directory. The second step typically uses realpath (or _fullpath on Windows) to normalize the user-specified path and compare it to a canonical base directory; if the resolved path is not under the base directory, the input is rejected.

For this specific file, we already have is_safe_relative_path doing basic syntactic validation. The best way to harden the code without changing intended functionality is:

  1. Determine a base directory. Since we have splice_getcwd defined as getcwd / _getcwd, we can use the process’s current working directory as the base.
  2. Canonicalize that base directory using realpath (or _fullpath on Windows) to avoid issues with symlinks and relative components.
  3. Canonicalize the user-supplied path (which is already checked to be relative) into an absolute path relative to the current working directory, again using realpath/_fullpath.
  4. Ensure that the canonicalized file path starts with the canonical base directory plus a path separator. If not, reject the input.
  5. Use the validated, canonical path when calling fopen instead of the raw argv[1].

We can implement this entirely inside main in src/splice.c, right after verifying the extension and before calling fopen. We will:

  • Add a small block to obtain and canonicalize the base directory.
  • Add another block to canonicalize path and check that it lies under the base directory.
  • Replace fopen(path, "rb") with fopen(resolved_path, "rb").
    We already include <limits.h> and <unistd.h>, and PATH_MAX is defined; we do not need new headers. We will use realpath directly, which is widely available on POSIX; for Windows builds, this project already uses _getcwd, and realpath is commonly available via POSIX-compatibility layers, but since we cannot modify unseen platform abstractions, we will still call realpath guarded by standard behavior and handle failure gracefully.
Suggested changeset 1
src/splice.c

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/splice.c b/src/splice.c
--- a/src/splice.c
+++ b/src/splice.c
@@ -103,13 +103,55 @@
         return 1;
     }
 
+    /* Resolve and constrain path to the current working directory to
+       mitigate path traversal and unintended file access. */
+    char cwd_buf[PATH_MAX];
+    if (splice_getcwd(cwd_buf, sizeof(cwd_buf)) == NULL) {
+        perror("getcwd");
+        return 1;
+    }
+
+    char *base_real = realpath(cwd_buf, NULL);
+    if (base_real == NULL) {
+        perror("realpath");
+        return 1;
+    }
+
+    char path_buf[PATH_MAX];
+    /* Build a path relative to the current working directory. */
+    if (snprintf(path_buf, sizeof(path_buf), "%s/%s", cwd_buf, path) >= (int)sizeof(path_buf)) {
+        fprintf(stderr, "[ERROR] SPC path too long\n");
+        free(base_real);
+        return 1;
+    }
+
+    char *resolved_path = realpath(path_buf, NULL);
+    if (resolved_path == NULL) {
+        perror("realpath");
+        free(base_real);
+        return 1;
+    }
+
+    size_t base_len = strlen(base_real);
+    /* Ensure the resolved file path resides within the base directory. */
+    if (strncmp(base_real, resolved_path, base_len) != 0 ||
+        (resolved_path[base_len] != '/' && resolved_path[base_len] != '\0')) {
+        fprintf(stderr, "[ERROR] SPC path escapes working directory\n");
+        free(base_real);
+        free(resolved_path);
+        return 1;
+    }
+
+    free(base_real);
+
     /* =========================
        Read SPC into memory
        ========================= */
 
-    FILE *f = fopen(path, "rb");
+    FILE *f = fopen(resolved_path, "rb");
     if (!f) {
         perror("fopen");
+        free(resolved_path);
         return 1;
     }
 
EOF
@@ -103,13 +103,55 @@
return 1;
}

/* Resolve and constrain path to the current working directory to
mitigate path traversal and unintended file access. */
char cwd_buf[PATH_MAX];
if (splice_getcwd(cwd_buf, sizeof(cwd_buf)) == NULL) {
perror("getcwd");
return 1;
}

char *base_real = realpath(cwd_buf, NULL);
if (base_real == NULL) {
perror("realpath");
return 1;
}

char path_buf[PATH_MAX];
/* Build a path relative to the current working directory. */
if (snprintf(path_buf, sizeof(path_buf), "%s/%s", cwd_buf, path) >= (int)sizeof(path_buf)) {
fprintf(stderr, "[ERROR] SPC path too long\n");
free(base_real);
return 1;
}

char *resolved_path = realpath(path_buf, NULL);
if (resolved_path == NULL) {
perror("realpath");
free(base_real);
return 1;
}

size_t base_len = strlen(base_real);
/* Ensure the resolved file path resides within the base directory. */
if (strncmp(base_real, resolved_path, base_len) != 0 ||
(resolved_path[base_len] != '/' && resolved_path[base_len] != '\0')) {
fprintf(stderr, "[ERROR] SPC path escapes working directory\n");
free(base_real);
free(resolved_path);
return 1;
}

free(base_real);

/* =========================
Read SPC into memory
========================= */

FILE *f = fopen(path, "rb");
FILE *f = fopen(resolved_path, "rb");
if (!f) {
perror("fopen");
free(resolved_path);
return 1;
}

Copilot is powered by AI and may make mistakes. Always verify output.
@Reboy20000
Copy link
Member Author

send another pull and i will review

@Reboy20000 Reboy20000 closed this Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant