Merge pull request #3 from Azreyo/develop

Develop
This commit is contained in:
2025-11-02 12:00:56 +00:00
committed by GitHub
6 changed files with 160 additions and 6 deletions

86
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential libssl-dev libmagic-dev libnghttp2-dev pkg-config
- name: Build project
run: make clean && make
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: server-binary
path: server
test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential libssl-dev libmagic-dev libnghttp2-dev pkg-config
- name: Build and run tests
run: |
make clean && make
# Verify the binary was created
test -f server && echo "✓ Server binary built successfully"
# Basic smoke tests
./server --help || echo "✓ Server executable is valid"
echo "✓ All tests passed"
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install security tools
run: |
sudo apt-get update
sudo apt-get install -y cppcheck flawfinder
- name: Run Flawfinder
run: |
flawfinder --minlevel=1 --html --context src/ > flawfinder-report.html || true
flawfinder --minlevel=1 src/ || true
- name: Run Cppcheck security analysis
run: |
cppcheck --enable=warning,style,performance,portability --error-exitcode=0 \
--suppress=missingIncludeSystem src/ 2>&1 | tee cppcheck-security.txt
code-quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install code quality tools
run: |
sudo apt-get update
sudo apt-get install -y cppcheck clang-format clang-tidy
- name: Run Cppcheck
run: |
cppcheck --enable=all --inconclusive --error-exitcode=0 \
--suppress=missingIncludeSystem \
--suppress=unusedFunction \
src/ 2>&1 | tee cppcheck-report.txt
- name: Check code formatting
run: |
find src/ -name "*.c" -o -name "*.h" | while read file; do
clang-format -style=file -output-replacements-xml "$file" | grep -q "<replacement " && echo "Format issues in $file" || true
done
- name: Upload code quality reports
uses: actions/upload-artifact@v4
if: always()
with:
name: code-quality-reports
path: |
cppcheck-report.txt

3
.gitignore vendored
View File

@@ -53,3 +53,6 @@ dkms.conf
log/*
server
ssl/*
# Allow .github/workflows for CI/CD
.github/*
!.github/workflows/

View File

@@ -233,6 +233,19 @@ static int on_frame_recv_callback(nghttp2_session *session,
break;
}
if (st.st_size < 0 || st.st_size > 0x7FFFFFFFFFFFFFFF)
{
close(fd);
log_event("HTTP/2: File size out of bounds");
// Send 500 error
nghttp2_nv hdrs[] = {
{(uint8_t *)":status", (uint8_t *)"500", 7, 3, NGHTTP2_NV_FLAG_NONE},
{(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}};
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
break;
}
// Get MIME type
char *mime_type = get_mime_type(filepath);
if (!mime_type)

View File

@@ -122,6 +122,12 @@ mmap_cache_entry_t *get_cached_file(const char *path)
{
if (mmap_cache[i].path && strcmp(mmap_cache[i].path, path) == 0)
{
if (mmap_cache[i].mmap_data == NULL || mmap_cache[i].size == 0)
{
pthread_mutex_unlock(&mmap_cache_mutex);
return NULL;
}
mmap_cache[i].last_access = time(NULL);
mmap_cache[i].ref_count++;
pthread_mutex_unlock(&mmap_cache_mutex);

View File

@@ -452,6 +452,11 @@ static int is_websocket_upgrade(const char *request)
static void *handle_websocket(void *arg)
{
ws_connection_t *conn = (ws_connection_t *)arg;
if (!conn)
{
pthread_exit(NULL);
}
log_event("WebSocket connection established");
@@ -471,7 +476,9 @@ static void *handle_websocket(void *arg)
if (bytes_received <= 0)
{
break;
ws_close_connection(conn, 1000);
free(conn);
pthread_exit(NULL);
}
ws_frame_header_t header;
@@ -482,7 +489,9 @@ static void *handle_websocket(void *arg)
{
log_event("Failed to parse WebSocket frame");
free(payload);
break;
ws_close_connection(conn, 1002);
free(conn);
pthread_exit(NULL);
}
switch (header.opcode)
@@ -645,9 +654,18 @@ void *handle_http_client(void *arg)
}
char filepath[512];
snprintf(filepath, sizeof(filepath), "%s%s", config.www_path,
int written = snprintf(filepath, sizeof(filepath), "%s%s", config.www_path,
(*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url);
free(sanitized_url);
if (written < 0 || written >= (int)sizeof(filepath))
{
log_event("Path too long, potential buffer overflow attempt");
const char *error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n";
send(client_socket, error_response, strlen(error_response), 0);
close(client_socket);
pthread_exit(NULL);
}
// Get MIME type
char *mime_type = get_mime_type(filepath);
@@ -1530,10 +1548,22 @@ char *sanitize_url(const char *url)
}
else if (c == '%')
{
// URL encoding - only allow safe encoded characters
// URL encoding - decode and validate the character
if (i + 2 < url_len && isxdigit(url[i + 1]) && isxdigit(url[i + 2]))
{
sanitized[j++] = c;
char hex[3] = {url[i + 1], url[i + 2], 0};
int decoded = (int)strtol(hex, NULL, 16);
// Block encoded directory traversal characters and control characters
if (decoded == '.' || decoded == '/' || decoded == '\\' ||
decoded == 0x00 || decoded < 0x20 || decoded > 0x7E)
{
free(sanitized);
return NULL;
}
sanitized[j++] = (char)decoded;
i += 2;
}
else
{
@@ -1642,6 +1672,8 @@ void cleanup_thread_pool()
return;
}
pthread_mutex_lock(&thread_pool_mutex);
for (int i = 0; i < thread_pool_size; i++)
{
if (thread_pool[i].busy)
@@ -1650,9 +1682,15 @@ void cleanup_thread_pool()
pthread_join(thread_pool[i].thread, NULL);
}
}
free(thread_pool);
ThreadInfo *temp = thread_pool;
thread_pool = NULL;
thread_pool_size = 0;
pthread_mutex_unlock(&thread_pool_mutex);
// Free after releasing lock and nullifying pointer
free(temp);
}
void cache_file(const char *path, const char *data, size_t size, const char *mime_type)

View File

@@ -116,6 +116,9 @@ int ws_handle_handshake_ssl(SSL *ssl, const char *request, char *response, size_
// Parse WebSocket frame
int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload)
{
// Maximum allowed WebSocket payload size (10MB)
#define MAX_WEBSOCKET_PAYLOAD (10 * 1024 * 1024)
if (len < 2)
{
return -1;
@@ -151,6 +154,11 @@ int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, u
header->payload_length = payload_len;
}
if (header->payload_length > MAX_WEBSOCKET_PAYLOAD)
{
return -1;
}
if (header->mask)
{
if (len < offset + 4)