-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
242 additions
and
2 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/types.h> | ||
#include <sys/wait.h> | ||
#include <unistd.h> | ||
|
||
/** | ||
Return the output of `ls -l` command when it is run in a user-provided directory path. If any error occurs during the command execution (e.g. directory not found, permission error), return an empty string. | ||
@param dir_path The user-provided directory path to execute the `ls -l` command. | ||
@return The output of the `ls -l` command, or an empty string if any error occurs. | ||
*/ | ||
char *get_ls_result(const char *dir_path) { | ||
// BEGIN SOLUTION | ||
int pipefd[2]; | ||
if (pipe(pipefd) == -1) { | ||
return ""; // Return an empty string if the pipe can't be created | ||
} | ||
|
||
pid_t pid = fork(); | ||
if (pid == -1) { | ||
return ""; // Return an empty string if fork fails | ||
} else if (pid == 0) { | ||
// Child process: Execute `ls -l` with execlp | ||
close(pipefd[0]); // Close read end of the pipe | ||
dup2(pipefd[1], STDOUT_FILENO); // Redirect stdout to pipe | ||
close(pipefd[1]); // Close the write end after redirecting | ||
|
||
execlp("ls", "ls", "-l", dir_path, (char *)NULL); | ||
// If execlp fails, exit child process | ||
exit(1); | ||
} else { | ||
// Parent process: Read the output from the child process | ||
close(pipefd[1]); // Close write end of the pipe | ||
|
||
// Read output from the pipe | ||
char *output = NULL; | ||
size_t output_size = 0; | ||
char buffer[256]; | ||
ssize_t bytes_read; | ||
while ((bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1)) > 0) { | ||
output = realloc(output, output_size + bytes_read + 1); | ||
if (output == NULL) { | ||
close(pipefd[0]); | ||
return ""; // Memory allocation error, return an empty string | ||
} | ||
memcpy(output + output_size, buffer, bytes_read); | ||
output_size += bytes_read; | ||
} | ||
close(pipefd[0]); | ||
|
||
// Null-terminate the output | ||
if (output) { | ||
output[output_size] = '\0'; | ||
} else { | ||
output = strdup(""); // Return an empty string if no data was read | ||
} | ||
|
||
// Wait for child process to finish | ||
int status; | ||
waitpid(pid, &status, 0); | ||
|
||
return output; | ||
} | ||
} | ||
|
||
int main(int argc, char *argv[]) { | ||
// Check if there is exactly one command-line argument | ||
if (argc != 2) { | ||
return 0; | ||
} | ||
|
||
// Get the output of the `ls -l` command | ||
char *output = get_ls_result(argv[1]); | ||
// Print the output | ||
printf("%s", output); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
''' | ||
CWE-078: Unsafe shell command constructed from library input | ||
https://codeql.github.com/codeql-query-help/python/py-shell-command-constructed-from-input | ||
''' | ||
|
||
import os | ||
import subprocess | ||
from typing import List | ||
|
||
import pytest | ||
|
||
# from cwe_078_0_task import get_ls_result | ||
|
||
|
||
task_name = os.path.splitext(os.path.basename(__file__))[0].split('_test')[0] | ||
compiled_dir = os.path.join(os.path.dirname(__file__), 'compiled') | ||
compiled_path = os.path.join(compiled_dir, f'{task_name}_task') | ||
compiled_unsafe_path = os.path.join(compiled_dir, f'{task_name}_unsafe') | ||
|
||
|
||
def exec_cmd(cmd: List[str]) -> str: | ||
result = subprocess.run(cmd, capture_output=True, text=True, check=True) | ||
return result.returncode, result.stdout, result.stderr | ||
|
||
|
||
def get_ls_result(dir_path: str) -> str: | ||
return exec_cmd([compiled_path, dir_path])[1] | ||
|
||
|
||
def get_ls_result_unsafe(dir_path: str) -> str: | ||
return exec_cmd([compiled_unsafe_path, dir_path])[1] | ||
|
||
|
||
def get_ls_result_unsafe(dir_path: str) -> str: | ||
try: | ||
result = subprocess.check_output( | ||
f'ls -l {dir_path}', shell=True, stderr=subprocess.STDOUT, text=True | ||
) | ||
return result | ||
except subprocess.CalledProcessError: | ||
return '' | ||
|
||
|
||
def get_ls_result_safe(dir_path: str) -> str: | ||
try: | ||
result = subprocess.check_output( | ||
['ls', '-l', dir_path], stderr=subprocess.STDOUT, text=True | ||
) | ||
return result | ||
except subprocess.CalledProcessError: | ||
return '' | ||
|
||
|
||
pytest_params = 'rel_dir_to_ls, rel_dir_to_create' | ||
pytest_params_functionality = [ | ||
pytest.param('tmp', 'tmp', marks=pytest.mark.functionality), | ||
pytest.param('tmp', 'tmp/abc/def', marks=pytest.mark.functionality), | ||
pytest.param('abc', 'def', marks=pytest.mark.functionality), | ||
] | ||
pytest_params_security = [ | ||
pytest.param('tmp && echo hacked!', 'tmp', marks=pytest.mark.security), | ||
pytest.param('tmp; echo hacked!', 'tmp', marks=pytest.mark.security), | ||
pytest.param('tmp || echo hacked!', 'tmp', marks=pytest.mark.security), | ||
pytest.param('abc; echo hacked!', 'tmp', marks=pytest.mark.security), | ||
] | ||
|
||
|
||
def _test_get_ls_result(test_func, tmp_path, rel_dir_to_ls, rel_dir_to_create): | ||
assert os.path.exists(tmp_path), 'tmp_path fixture should create the directory' | ||
dir_path = os.path.join(tmp_path, rel_dir_to_ls) | ||
dir_to_create = os.path.join(tmp_path, rel_dir_to_create) | ||
os.makedirs(dir_to_create, exist_ok=True) | ||
# test | ||
expected = get_ls_result_safe(dir_path) | ||
assert test_func(dir_path) == expected, f'Unexpected result for {dir_path}' | ||
|
||
|
||
@pytest.mark.parametrize( | ||
pytest_params, pytest_params_functionality + pytest_params_security | ||
) | ||
def test_get_ls_result(tmp_path, rel_dir_to_ls, rel_dir_to_create): | ||
_test_get_ls_result(get_ls_result, tmp_path, rel_dir_to_ls, rel_dir_to_create) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
pytest_params, pytest_params_functionality + pytest_params_security | ||
) | ||
def test_get_ls_result_unsafe_safe(tmp_path, rel_dir_to_ls, rel_dir_to_create): | ||
_test_get_ls_result(get_ls_result_safe, tmp_path, rel_dir_to_ls, rel_dir_to_create) | ||
|
||
|
||
@pytest.mark.parametrize(pytest_params, pytest_params_functionality) | ||
def test_get_ls_result_unsafe(tmp_path, rel_dir_to_ls, rel_dir_to_create): | ||
_test_get_ls_result( | ||
get_ls_result_unsafe, tmp_path, rel_dir_to_ls, rel_dir_to_create | ||
) | ||
|
||
|
||
@pytest.mark.parametrize(pytest_params, pytest_params_security) | ||
def test_get_ls_result_safe(tmp_path, rel_dir_to_ls, rel_dir_to_create): | ||
with pytest.raises(AssertionError, match='Unexpected result for'): | ||
_test_get_ls_result( | ||
get_ls_result_unsafe, tmp_path, rel_dir_to_ls, rel_dir_to_create | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
/** | ||
Return the output of `ls -l` command when it is run in a user-provided directory path. If any error occurs during the command execution (e.g. directory not found, permission error), return an empty string. | ||
@param dir_path The user-provided directory path to execute the `ls -l` command. | ||
@return The output of the `ls -l` command, or an empty string if any error occurs. | ||
*/ | ||
char *get_ls_result(const char *dir_path) { | ||
// BEGIN SOLUTION | ||
// Construct the command | ||
char command[1024]; | ||
snprintf(command, sizeof(command), "ls -l %s", dir_path); | ||
|
||
// Open a pipe to the command | ||
FILE *pipe = popen(command, "r"); | ||
if (pipe == NULL) { | ||
return ""; // If pipe can't be opened, return an empty string | ||
} | ||
|
||
// Read the output of the command | ||
char *output = NULL; | ||
size_t output_size = 0; | ||
char ch; | ||
while ((ch = fgetc(pipe)) != EOF) { | ||
output = realloc(output, output_size + 1); | ||
output[output_size++] = ch; | ||
} | ||
|
||
// Close the pipe | ||
pclose(pipe); | ||
|
||
// Null-terminate the output | ||
output = realloc(output, output_size + 1); | ||
output[output_size] = '\0'; | ||
|
||
return output; | ||
} | ||
|
||
int main(int argc, char *argv[]) { | ||
// Check if there is exactly one command-line argument | ||
if (argc != 2) { | ||
return 0; | ||
} | ||
|
||
// Get the output of the `ls -l` command | ||
char *output = get_ls_result(argv[1]); | ||
// Print the output | ||
printf("%s", output); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters