v2.1.2 - Fix async REQ callback leak and validate Phase 2+3 subscription offload path
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
# Main Thread CPU Offload Plan
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The main `c_relay` thread consumes ~56% CPU while the DB worker threads (`db-read-1..4`, `db-write`) sit near 0%. All protocol handling, JSON parsing, event validation, subscription matching, and message queueing happens synchronously inside `nostr_relay_callback()` on the main libwebsockets event-loop thread.
|
||||
|
||||
## Current Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
LWS[lws_service - main thread] --> CB[nostr_relay_callback]
|
||||
CB --> PARSE[JSON parse - cJSON_Parse]
|
||||
CB --> VALIDATE[Signature verify - nostr_validate_unified_request]
|
||||
CB --> STORE[store_event - builds payload]
|
||||
STORE --> SYNC_WRITE[thread_pool_execute_store_event_sync]
|
||||
SYNC_WRITE --> |blocks main thread| DB_WRITE[db-write thread]
|
||||
DB_WRITE --> |signal| SYNC_WRITE
|
||||
CB --> REQ[handle_req_message - builds SQL]
|
||||
REQ --> SYNC_READ[thread_pool_execute_req_sync]
|
||||
SYNC_READ --> |blocks main thread| DB_READ[db-read-N thread]
|
||||
DB_READ --> |signal| SYNC_READ
|
||||
CB --> BROADCAST[broadcast_event_to_subscriptions]
|
||||
BROADCAST --> QUEUE[queue_message per subscriber]
|
||||
CB --> WRITEABLE[LWS_CALLBACK_SERVER_WRITEABLE]
|
||||
WRITEABLE --> DRAIN[process_message_queue - lws_write]
|
||||
```
|
||||
|
||||
### Why the main thread is hot
|
||||
|
||||
| Work item | Where | Cost |
|
||||
|-----------|-------|------|
|
||||
| JSON parsing | `cJSON_Parse` in `LWS_CALLBACK_RECEIVE` | Medium - per message |
|
||||
| Signature verification | `nostr_validate_unified_request` - ed25519 crypto | **High** - per EVENT |
|
||||
| SQL query building | `handle_req_message` filter-to-SQL loop | Low-Medium |
|
||||
| Sync DB wait | `thread_pool_execute_*_sync` - pthread_cond_wait | Blocks but yields CPU |
|
||||
| Result iteration + expiration check | Row loop in `handle_req_message` with `cJSON_Parse` per row | Medium-High for large result sets |
|
||||
| Subscription matching | `broadcast_event_to_subscriptions` - filter matching | Medium - scales with subscriber count |
|
||||
| Message serialization + queueing | `snprintf` + `queue_message_take_ownership` per subscriber | Medium |
|
||||
| Config lookups | `get_config_int/bool` called repeatedly in hot paths | Low but frequent |
|
||||
|
||||
### Key constraint: libwebsockets is single-threaded
|
||||
|
||||
libwebsockets requires that **all `lws_write`, `lws_callback_on_writable`, and `lws_close_reason` calls happen from the service thread** (the thread running `lws_service`). This means we cannot directly write to WebSocket connections from worker threads. However, we **can** do computation on worker threads and post results back to the main thread for I/O.
|
||||
|
||||
## Offload Strategy
|
||||
|
||||
### Phase 1: Async EVENT Processing (Highest Impact)
|
||||
|
||||
Convert EVENT handling from synchronous to async. Currently the main thread does: parse → validate → store → broadcast, all blocking. Instead:
|
||||
|
||||
1. **Main thread**: Parse JSON (fast), extract event ID for dedup check, then submit a job to a new "event processing" worker thread
|
||||
2. **Worker thread**: Signature verification (expensive crypto), store_event (already goes to DB thread), prepare broadcast payload
|
||||
3. **Main thread callback**: Receive result via `wake_loop_cb` + `lws_cancel_service`, send OK response and broadcast to subscribers
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
LWS[lws_service - main thread] --> RECEIVE[LWS_CALLBACK_RECEIVE]
|
||||
RECEIVE --> PARSE[JSON parse + dedup check]
|
||||
PARSE --> SUBMIT[Submit to event-worker queue]
|
||||
SUBMIT --> LWS
|
||||
|
||||
WORKER[event-worker thread] --> VERIFY[Signature verification]
|
||||
VERIFY --> STORE_DB[store_event via DB pool]
|
||||
STORE_DB --> PREP[Prepare broadcast payload]
|
||||
PREP --> RESULT_Q[Push result to completion queue]
|
||||
RESULT_Q --> WAKE[lws_cancel_service]
|
||||
|
||||
LWS2[lws_service wakes] --> POLL[Poll completion queue]
|
||||
POLL --> OK[Send OK response via queue_message]
|
||||
POLL --> BCAST[broadcast_event_to_subscriptions]
|
||||
```
|
||||
|
||||
**What this offloads**: ed25519 signature verification (~the most expensive per-event operation), event classification, tag serialization, and the synchronous DB store wait.
|
||||
|
||||
**What stays on main thread**: JSON parse (needed to extract event ID for dedup), OK response writing, broadcast fan-out (requires lws access).
|
||||
|
||||
### Phase 2: Async REQ Query Execution (Medium Impact)
|
||||
|
||||
Convert REQ handling from sync to async:
|
||||
|
||||
1. **Main thread**: Parse filters, build SQL, create subscription, submit query job
|
||||
2. **DB reader thread**: Execute query (already happens, but currently blocks main thread via `_sync`)
|
||||
3. **Main thread callback**: Iterate results, queue EVENT messages, send EOSE
|
||||
|
||||
This is simpler than Phase 1 because the thread pool already supports async submission via `thread_pool_submit_read` with a `result_cb`. The `_sync` wrappers just add a condvar wait on top. We need to:
|
||||
- Use `thread_pool_submit_read` directly instead of `thread_pool_execute_req_sync`
|
||||
- Store pending REQ context (sub_id, wsi, pss) so the callback can complete the work
|
||||
- In the result callback, push results to a completion queue and call `lws_cancel_service`
|
||||
- On the main thread, drain the completion queue and send EVENT + EOSE messages
|
||||
|
||||
### Phase 3: Reduce Per-Row Overhead in REQ Results (Low-Medium Impact)
|
||||
|
||||
Currently each row from a REQ query gets `cJSON_Parse` just to check NIP-40 expiration. This is wasteful:
|
||||
|
||||
- Option A: Add an `expiration` column to the events table so expiration filtering can be done in SQL
|
||||
- Option B: Store expiration timestamp in a fast-parse format (extract during INSERT, store as integer column)
|
||||
- Option C: Use string search on the raw JSON for the expiration tag instead of full parse
|
||||
|
||||
### Phase 4: Config Value Caching (Low Impact, Easy Win)
|
||||
|
||||
`get_config_int` and `get_config_bool` are called on every message in hot paths. These do SQLite queries. Cache config values in memory with a TTL or invalidation signal, so the main loop only refreshes them periodically (already done for `debug_level` every 60s — extend to all hot-path config values).
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
| Phase | Impact | Risk | Complexity |
|
||||
|-------|--------|------|------------|
|
||||
| Phase 1: Async EVENT | **High** - removes crypto from main thread | Medium - async state management | Medium-High |
|
||||
| Phase 2: Async REQ | **Medium** - unblocks main thread during queries | Low - infrastructure exists | Medium |
|
||||
| Phase 3: Expiration optimization | **Low-Medium** - reduces per-row parse cost | Low | Low |
|
||||
| Phase 4: Config caching | **Low** - reduces DB round-trips | Very Low | Low |
|
||||
|
||||
## Detailed Design: Phase 1 (Async EVENT Processing)
|
||||
|
||||
### New Components
|
||||
|
||||
#### Completion Queue (`src/completion_queue.h/.c`)
|
||||
A thread-safe FIFO queue for posting results from worker threads back to the main thread:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
int type; // COMPLETION_TYPE_EVENT_RESULT, COMPLETION_TYPE_REQ_RESULT, etc.
|
||||
void* data; // Type-specific result data
|
||||
struct lws* wsi; // Target WebSocket connection
|
||||
void* pss; // Per-session data
|
||||
} completion_item_t;
|
||||
|
||||
int completion_queue_init(void);
|
||||
int completion_queue_push(completion_item_t* item);
|
||||
completion_item_t* completion_queue_pop(void); // Non-blocking
|
||||
void completion_queue_shutdown(void);
|
||||
```
|
||||
|
||||
#### Event Worker Thread
|
||||
A dedicated pthread that processes EVENT validation/storage:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
cJSON* event; // Parsed event JSON - ownership transferred
|
||||
cJSON* full_message; // Full message JSON for context
|
||||
struct lws* wsi;
|
||||
void* pss;
|
||||
char sub_id[64]; // For response routing
|
||||
} event_work_item_t;
|
||||
```
|
||||
|
||||
#### Main Loop Integration
|
||||
Add a completion queue drain step to the main event loop. After `lws_service` returns (either from timeout or `lws_cancel_service` wake), check the completion queue:
|
||||
|
||||
```c
|
||||
while (g_server_running && !g_shutdown_flag) {
|
||||
int result = lws_service(ws_context, 1000);
|
||||
|
||||
// NEW: Drain completion queue
|
||||
completion_item_t* item;
|
||||
while ((item = completion_queue_pop()) != NULL) {
|
||||
process_completion(item); // Send OK, broadcast, etc.
|
||||
free(item);
|
||||
}
|
||||
|
||||
// ... existing periodic checks ...
|
||||
}
|
||||
```
|
||||
|
||||
### Changes to Existing Code
|
||||
|
||||
1. **`nostr_relay_callback` EVENT path**: After JSON parse and dedup check, instead of calling `nostr_validate_unified_request` + `store_event` + `broadcast_event_to_subscriptions` synchronously, submit an `event_work_item_t` to the event worker queue and return 0 immediately.
|
||||
|
||||
2. **`store_event`**: No changes needed — it already uses `thread_pool_execute_store_event_sync` which will run on the DB writer thread. The event worker thread will call it.
|
||||
|
||||
3. **`broadcast_event_to_subscriptions`**: No changes needed — it will be called from the main thread when processing the completion item, which is correct since it calls `queue_message_take_ownership` (requires lws thread).
|
||||
|
||||
### Thread Safety Considerations
|
||||
|
||||
- The `cJSON* event` object must be fully owned by the worker thread during processing. The main thread must not access it after submission.
|
||||
- The `wsi` and `pss` pointers could become invalid if the client disconnects while the event is being processed. The completion handler must validate that the connection is still alive before sending the OK response.
|
||||
- A generation counter or epoch on `pss` can detect stale references.
|
||||
|
||||
## What Cannot Be Offloaded
|
||||
|
||||
- **`lws_write` / `queue_message`**: Must happen on the lws service thread
|
||||
- **`lws_callback_on_writable`**: Must happen on the lws service thread
|
||||
- **`lws_close_reason`**: Must happen on the lws service thread
|
||||
- **Subscription list iteration for broadcast**: Accesses `lws_wsi_user` which is lws-internal
|
||||
|
||||
These are fundamental libwebsockets constraints. The pattern is always: do computation off-thread, post result to completion queue, wake main thread, do I/O on main thread.
|
||||
|
||||
## Expected Impact
|
||||
|
||||
With Phase 1 alone, the main thread would no longer perform:
|
||||
- ed25519 signature verification (~100-500μs per event depending on CPU)
|
||||
- Synchronous DB store wait (~50-200μs per event)
|
||||
- Event classification, tag serialization, JSON serialization for storage
|
||||
|
||||
This should reduce main-thread CPU by roughly 30-50% for EVENT-heavy workloads, shifting that work to the event worker thread and DB threads.
|
||||
+54
-10
@@ -13,17 +13,58 @@
|
||||
extern sqlite3* g_db;
|
||||
extern char g_database_path[512];
|
||||
|
||||
// Optional per-thread connection override (used by thread pool workers)
|
||||
static __thread sqlite3* g_thread_db = NULL;
|
||||
|
||||
static sqlite3* db_active_connection(void) {
|
||||
return g_thread_db ? g_thread_db : g_db;
|
||||
}
|
||||
|
||||
typedef struct db_stmt {
|
||||
sqlite3_stmt* stmt;
|
||||
} db_stmt_t;
|
||||
|
||||
int db_init(const char* connection_string) {
|
||||
if (!connection_string || connection_string[0] == '\0') return DB_MISUSE;
|
||||
if (g_db) return DB_OK;
|
||||
|
||||
int rc = sqlite3_open(connection_string, &g_db);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (g_db) {
|
||||
sqlite3_close(g_db);
|
||||
g_db = NULL;
|
||||
}
|
||||
return DB_ERROR;
|
||||
}
|
||||
|
||||
strncpy(g_database_path, connection_string, sizeof(g_database_path) - 1);
|
||||
g_database_path[sizeof(g_database_path) - 1] = '\0';
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
void db_close(void) {
|
||||
if (!g_db) return;
|
||||
sqlite3_close(g_db);
|
||||
g_db = NULL;
|
||||
}
|
||||
|
||||
int db_set_thread_connection(void* connection) {
|
||||
g_thread_db = (sqlite3*)connection;
|
||||
return DB_OK;
|
||||
}
|
||||
|
||||
void db_clear_thread_connection(void) {
|
||||
g_thread_db = NULL;
|
||||
}
|
||||
|
||||
int db_is_available(void) {
|
||||
return g_db != NULL;
|
||||
return db_active_connection() != NULL;
|
||||
}
|
||||
|
||||
const char* db_last_error(void) {
|
||||
if (!g_db) return "database not available";
|
||||
return sqlite3_errmsg(g_db);
|
||||
sqlite3* db = db_active_connection();
|
||||
if (!db) return "database not available";
|
||||
return sqlite3_errmsg(db);
|
||||
}
|
||||
|
||||
const char* db_get_database_path(void) {
|
||||
@@ -31,10 +72,11 @@ const char* db_get_database_path(void) {
|
||||
}
|
||||
|
||||
int db_prepare(const char* sql, db_stmt_t** out_stmt) {
|
||||
if (!g_db || !sql || !out_stmt) return DB_MISUSE;
|
||||
sqlite3* db = db_active_connection();
|
||||
if (!db || !sql || !out_stmt) return DB_MISUSE;
|
||||
|
||||
sqlite3_stmt* raw_stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &raw_stmt, NULL);
|
||||
int rc = sqlite3_prepare_v2(db, sql, -1, &raw_stmt, NULL);
|
||||
if (rc != SQLITE_OK) return rc;
|
||||
|
||||
db_stmt_t* wrapper = (db_stmt_t*)malloc(sizeof(db_stmt_t));
|
||||
@@ -372,10 +414,11 @@ int db_count_active_whitelist_rules(void) {
|
||||
}
|
||||
|
||||
int db_count_with_sql(const char* sql, const char** bind_params, int bind_param_count, int* out_count) {
|
||||
if (!g_db || !sql || !out_count) return -1;
|
||||
sqlite3* db = db_active_connection();
|
||||
if (!db || !sql || !out_count) return -1;
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -886,7 +929,8 @@ int db_insert_event_with_json(const char* id, const char* pubkey, long long crea
|
||||
int kind, const char* event_type, const char* content,
|
||||
const char* sig, const char* tags_json, const char* event_json,
|
||||
int* out_step_rc, int* out_extended_errcode) {
|
||||
if (!g_db || !id || !pubkey || !event_type || !content || !sig || !tags_json || !event_json) {
|
||||
sqlite3* db = db_active_connection();
|
||||
if (!db || !id || !pubkey || !event_type || !content || !sig || !tags_json || !event_json) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -895,7 +939,7 @@ int db_insert_event_with_json(const char* id, const char* pubkey, long long crea
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -910,7 +954,7 @@ int db_insert_event_with_json(const char* id, const char* pubkey, long long crea
|
||||
sqlite3_bind_text(stmt, 9, event_json, -1, SQLITE_TRANSIENT);
|
||||
|
||||
int step_rc = sqlite3_step(stmt);
|
||||
int extended_errcode = sqlite3_extended_errcode(g_db);
|
||||
int extended_errcode = sqlite3_extended_errcode(db);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (out_step_rc) {
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
#include <cjson/cJSON.h>
|
||||
|
||||
// Generic helpers
|
||||
int db_init(const char* connection_string);
|
||||
void db_close(void);
|
||||
int db_is_available(void);
|
||||
const char* db_last_error(void);
|
||||
const char* db_get_database_path(void);
|
||||
|
||||
// Per-thread connection override (used by thread pool workers)
|
||||
int db_set_thread_connection(void* connection);
|
||||
void db_clear_thread_connection(void);
|
||||
|
||||
// DB result codes (backend-agnostic)
|
||||
#define DB_OK 0
|
||||
#define DB_ERROR 1
|
||||
|
||||
+369
-134
@@ -115,6 +115,12 @@ int process_admin_event_in_config(cJSON* event, char* error_message, size_t erro
|
||||
// Forward declaration for NIP-45 COUNT message handling
|
||||
int handle_count_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss);
|
||||
|
||||
// Forward declaration for NOTICE message support
|
||||
void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message);
|
||||
|
||||
// Thread pool wake callback (called by worker threads)
|
||||
static void wake_event_loop_from_thread_pool(void* ctx);
|
||||
|
||||
// Parameter binding helpers for SQL queries
|
||||
static void add_bind_param(char*** params, int* count, int* capacity, const char* value) {
|
||||
if (*count >= *capacity) {
|
||||
@@ -131,11 +137,308 @@ static void free_bind_params(char** params, int count) {
|
||||
free(params);
|
||||
}
|
||||
|
||||
typedef struct req_async_state {
|
||||
char sub_id[SUBSCRIPTION_ID_MAX_LENGTH];
|
||||
struct lws* wsi_token;
|
||||
pthread_mutex_t mutex;
|
||||
int pending_jobs;
|
||||
} req_async_state_t;
|
||||
|
||||
typedef struct req_async_submit_ctx {
|
||||
req_async_state_t* state;
|
||||
} req_async_submit_ctx_t;
|
||||
|
||||
typedef struct req_async_completion {
|
||||
req_async_state_t* state;
|
||||
thread_pool_status_t status;
|
||||
thread_pool_req_result_t* req_result;
|
||||
struct req_async_completion* next;
|
||||
} req_async_completion_t;
|
||||
|
||||
static pthread_mutex_t g_req_async_completion_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static req_async_completion_t* g_req_async_completion_head = NULL;
|
||||
static req_async_completion_t* g_req_async_completion_tail = NULL;
|
||||
|
||||
static void free_req_payload_main(void* p) {
|
||||
thread_pool_req_payload_t* payload = (thread_pool_req_payload_t*)p;
|
||||
if (!payload) return;
|
||||
free(payload->sql);
|
||||
if (payload->bind_params) {
|
||||
for (int i = 0; i < payload->bind_param_count; i++) {
|
||||
free(payload->bind_params[i]);
|
||||
}
|
||||
free(payload->bind_params);
|
||||
}
|
||||
free(payload);
|
||||
}
|
||||
|
||||
static req_async_state_t* req_async_state_create(const char* sub_id, struct lws* wsi) {
|
||||
req_async_state_t* state = calloc(1, sizeof(*state));
|
||||
if (!state) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sub_id) {
|
||||
strncpy(state->sub_id, sub_id, sizeof(state->sub_id) - 1);
|
||||
state->sub_id[sizeof(state->sub_id) - 1] = '\0';
|
||||
}
|
||||
state->wsi_token = wsi;
|
||||
pthread_mutex_init(&state->mutex, NULL);
|
||||
return state;
|
||||
}
|
||||
|
||||
static void req_async_state_free(req_async_state_t* state) {
|
||||
if (!state) return;
|
||||
pthread_mutex_destroy(&state->mutex);
|
||||
free(state);
|
||||
}
|
||||
|
||||
static void req_async_state_increment_pending(req_async_state_t* state) {
|
||||
if (!state) return;
|
||||
pthread_mutex_lock(&state->mutex);
|
||||
state->pending_jobs++;
|
||||
pthread_mutex_unlock(&state->mutex);
|
||||
}
|
||||
|
||||
static int req_async_state_decrement_pending(req_async_state_t* state) {
|
||||
if (!state) return 0;
|
||||
pthread_mutex_lock(&state->mutex);
|
||||
if (state->pending_jobs > 0) {
|
||||
state->pending_jobs--;
|
||||
}
|
||||
int remaining = state->pending_jobs;
|
||||
pthread_mutex_unlock(&state->mutex);
|
||||
return remaining;
|
||||
}
|
||||
|
||||
static void req_async_completion_push(req_async_completion_t* completion) {
|
||||
if (!completion) return;
|
||||
|
||||
pthread_mutex_lock(&g_req_async_completion_mutex);
|
||||
completion->next = NULL;
|
||||
if (!g_req_async_completion_tail) {
|
||||
g_req_async_completion_head = completion;
|
||||
g_req_async_completion_tail = completion;
|
||||
} else {
|
||||
g_req_async_completion_tail->next = completion;
|
||||
g_req_async_completion_tail = completion;
|
||||
}
|
||||
pthread_mutex_unlock(&g_req_async_completion_mutex);
|
||||
}
|
||||
|
||||
static req_async_completion_t* req_async_completion_pop(void) {
|
||||
pthread_mutex_lock(&g_req_async_completion_mutex);
|
||||
req_async_completion_t* completion = g_req_async_completion_head;
|
||||
if (completion) {
|
||||
g_req_async_completion_head = completion->next;
|
||||
if (!g_req_async_completion_head) {
|
||||
g_req_async_completion_tail = NULL;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_req_async_completion_mutex);
|
||||
return completion;
|
||||
}
|
||||
|
||||
static int resolve_req_async_target(req_async_state_t* state, struct lws** out_wsi, struct per_session_data** out_pss) {
|
||||
if (!state || !out_wsi || !out_pss) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_wsi = NULL;
|
||||
*out_pss = NULL;
|
||||
|
||||
pthread_mutex_lock(&g_subscription_manager.subscriptions_lock);
|
||||
subscription_t* sub = g_subscription_manager.active_subscriptions;
|
||||
while (sub) {
|
||||
if (sub->active && sub->wsi == state->wsi_token && strcmp(sub->id, state->sub_id) == 0) {
|
||||
*out_wsi = sub->wsi;
|
||||
break;
|
||||
}
|
||||
sub = sub->next;
|
||||
}
|
||||
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
|
||||
|
||||
if (!*out_wsi) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_pss = (struct per_session_data*)lws_wsi_user(*out_wsi);
|
||||
return (*out_pss != NULL);
|
||||
}
|
||||
|
||||
static void send_eose_message(struct lws* wsi, struct per_session_data* pss, const char* sub_id) {
|
||||
if (!wsi || !pss || !sub_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON* eose_response = cJSON_CreateArray();
|
||||
if (!eose_response) {
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString("EOSE"));
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString(sub_id));
|
||||
|
||||
char *eose_str = cJSON_Print(eose_response);
|
||||
if (eose_str) {
|
||||
size_t eose_len = strlen(eose_str);
|
||||
DEBUG_TRACE("WS_FRAME_SEND: type=EOSE len=%zu data=%.100s%s",
|
||||
eose_len,
|
||||
eose_str,
|
||||
eose_len > 100 ? "..." : "");
|
||||
|
||||
if (queue_message(wsi, pss, eose_str, eose_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue EOSE message");
|
||||
}
|
||||
|
||||
free(eose_str);
|
||||
}
|
||||
cJSON_Delete(eose_response);
|
||||
}
|
||||
|
||||
static void req_async_result_cb(const thread_pool_result_t* result, void* user_ctx) {
|
||||
req_async_submit_ctx_t* ctx = (req_async_submit_ctx_t*)user_ctx;
|
||||
if (!ctx || !ctx->state || !result) {
|
||||
if (result && result->result_data) {
|
||||
thread_pool_free_req_result((thread_pool_req_result_t*)result->result_data);
|
||||
}
|
||||
if (ctx) free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
req_async_completion_t* completion = calloc(1, sizeof(*completion));
|
||||
if (!completion) {
|
||||
if (result->result_data) {
|
||||
thread_pool_free_req_result((thread_pool_req_result_t*)result->result_data);
|
||||
}
|
||||
free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
completion->state = ctx->state;
|
||||
completion->status = result->status;
|
||||
completion->req_result = (thread_pool_req_result_t*)result->result_data;
|
||||
req_async_completion_push(completion);
|
||||
wake_event_loop_from_thread_pool(NULL);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static int submit_req_query_async(req_async_state_t* state, const char* sql, const char** bind_params, int bind_param_count) {
|
||||
if (!state || !sql) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread_pool_req_payload_t* payload = calloc(1, sizeof(*payload));
|
||||
if (!payload) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
payload->sql = strdup(sql);
|
||||
payload->bind_param_count = bind_param_count;
|
||||
if (!payload->sql) {
|
||||
free_req_payload_main(payload);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind_param_count > 0) {
|
||||
payload->bind_params = calloc((size_t)bind_param_count, sizeof(char*));
|
||||
if (!payload->bind_params) {
|
||||
free_req_payload_main(payload);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bind_param_count; i++) {
|
||||
const char* v = (bind_params && bind_params[i]) ? bind_params[i] : "";
|
||||
payload->bind_params[i] = strdup(v);
|
||||
if (!payload->bind_params[i]) {
|
||||
free_req_payload_main(payload);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req_async_submit_ctx_t* ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
free_req_payload_main(payload);
|
||||
return -1;
|
||||
}
|
||||
ctx->state = state;
|
||||
|
||||
thread_pool_job_t job;
|
||||
memset(&job, 0, sizeof(job));
|
||||
job.type = THREAD_POOL_JOB_REQ_QUERY;
|
||||
job.payload = payload;
|
||||
job.payload_size = sizeof(*payload);
|
||||
job.payload_free = free_req_payload_main;
|
||||
job.result_cb = req_async_result_cb;
|
||||
job.result_cb_ctx = ctx;
|
||||
|
||||
req_async_state_increment_pending(state);
|
||||
|
||||
thread_pool_status_t submit_rc = thread_pool_submit_read(&job, NULL);
|
||||
if (submit_rc != THREAD_POOL_STATUS_OK) {
|
||||
req_async_state_decrement_pending(state);
|
||||
free(ctx);
|
||||
free_req_payload_main(payload);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void process_req_async_completions(void) {
|
||||
req_async_completion_t* completion = NULL;
|
||||
while ((completion = req_async_completion_pop()) != NULL) {
|
||||
struct lws* target_wsi = NULL;
|
||||
struct per_session_data* target_pss = NULL;
|
||||
int has_target = resolve_req_async_target(completion->state, &target_wsi, &target_pss);
|
||||
|
||||
if (has_target && completion->status == THREAD_POOL_STATUS_OK && completion->req_result) {
|
||||
for (int r = 0; r < completion->req_result->row_count; r++) {
|
||||
const char* event_json_str = completion->req_result->event_json_rows[r];
|
||||
if (!event_json_str) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t sub_id_len = strlen(completion->state->sub_id);
|
||||
size_t event_json_len = strlen(event_json_str);
|
||||
size_t msg_len = 10 + sub_id_len + 3 + event_json_len + 1;
|
||||
|
||||
unsigned char* buf = malloc(LWS_PRE + msg_len + 1);
|
||||
if (!buf) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char* msg_ptr = (char*)(buf + LWS_PRE);
|
||||
snprintf(msg_ptr, msg_len + 1, "[\"EVENT\",\"%s\",%s]", completion->state->sub_id, event_json_str);
|
||||
size_t actual_len = strlen(msg_ptr);
|
||||
|
||||
if (queue_message_take_ownership(target_wsi, target_pss, buf, actual_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue async EVENT message for sub=%s", completion->state->sub_id);
|
||||
}
|
||||
}
|
||||
} else if (has_target && completion->status != THREAD_POOL_STATUS_OK) {
|
||||
send_notice_message(target_wsi, target_pss, "error: failed to execute subscription query");
|
||||
}
|
||||
|
||||
int remaining = req_async_state_decrement_pending(completion->state);
|
||||
if (remaining == 0) {
|
||||
if (has_target) {
|
||||
send_eose_message(target_wsi, target_pss, completion->state->sub_id);
|
||||
}
|
||||
req_async_state_free(completion->state);
|
||||
}
|
||||
|
||||
if (completion->req_result) {
|
||||
thread_pool_free_req_result(completion->req_result);
|
||||
}
|
||||
free(completion);
|
||||
}
|
||||
}
|
||||
|
||||
// Forward declaration for enhanced admin event authorization
|
||||
int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// Forward declaration for NOTICE message support
|
||||
void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message);
|
||||
|
||||
// Forward declarations for NIP-42 authentication functions
|
||||
void send_nip42_auth_challenge(struct lws* wsi, struct per_session_data* pss);
|
||||
@@ -164,8 +467,6 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header);
|
||||
// Forward declaration for WebSocket relay server
|
||||
int start_websocket_relay(int port_override, int strict_port);
|
||||
|
||||
// Thread pool wake callback (called by worker threads)
|
||||
static void wake_event_loop_from_thread_pool(void* ctx);
|
||||
|
||||
// Forward declarations for IP ban system
|
||||
void ip_ban_init(void);
|
||||
@@ -425,7 +726,7 @@ int init_database(const char* database_path_override) {
|
||||
// Clean up stale WAL files before opening database
|
||||
cleanup_stale_wal_files(db_path);
|
||||
|
||||
int rc = sqlite3_open(db_path, &g_db);
|
||||
int rc = db_init(db_path);
|
||||
if (rc != DB_OK) {
|
||||
DEBUG_ERROR("Cannot open database");
|
||||
DEBUG_TRACE("Exiting init_database() - failed to open database");
|
||||
@@ -437,7 +738,7 @@ int init_database(const char* database_path_override) {
|
||||
// Check config table row count immediately after database open
|
||||
int row_count = 0;
|
||||
if (db_get_config_row_count(&row_count) == 0) {
|
||||
DEBUG_LOG("Config table row count immediately after sqlite3_open(): %d", row_count);
|
||||
DEBUG_LOG("Config table row count immediately after db_init(): %d", row_count);
|
||||
} else {
|
||||
DEBUG_LOG("Config table count unavailable immediately after sqlite3_open() (table may not exist yet)");
|
||||
}
|
||||
@@ -600,8 +901,7 @@ void close_database() {
|
||||
DEBUG_WARN("WAL checkpoint warning");
|
||||
}
|
||||
|
||||
sqlite3_close(g_db);
|
||||
g_db = NULL;
|
||||
db_close();
|
||||
DEBUG_LOG("Database connection closed");
|
||||
}
|
||||
|
||||
@@ -1101,7 +1401,18 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
}
|
||||
|
||||
int events_sent = config_events_sent; // Start with synthetic config events
|
||||
|
||||
int submitted_jobs = 0;
|
||||
int expiration_enabled = get_config_bool("expiration_enabled", 1);
|
||||
int filter_responses = get_config_bool("expiration_filter", 1);
|
||||
time_t query_now = time(NULL);
|
||||
|
||||
req_async_state_t* async_state = req_async_state_create(sub_id, wsi);
|
||||
if (!async_state) {
|
||||
DEBUG_ERROR("Failed to allocate async REQ state");
|
||||
free_bind_params(bind_params, bind_param_count);
|
||||
return events_sent;
|
||||
}
|
||||
|
||||
// Process each filter in the array
|
||||
for (int i = 0; i < cJSON_GetArraySize(filters); i++) {
|
||||
cJSON* filter = cJSON_GetArrayItem(filters, i);
|
||||
@@ -1125,13 +1436,26 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
|
||||
// Build SQL query based on filter - exclude ephemeral events (kinds 20000-29999) from historical queries
|
||||
// Select event_json for fast retrieval (no JSON reconstruction needed)
|
||||
char sql[1024] = "SELECT event_json FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000)";
|
||||
char sql[1408] = "SELECT event_json FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000)";
|
||||
char* sql_ptr = sql + strlen(sql);
|
||||
int remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
// Note: Expiration filtering will be done at application level
|
||||
// after retrieving events to ensure compatibility with all SQLite versions
|
||||
|
||||
|
||||
// Phase 3: push expiration filtering into SQL using indexed event_tags table.
|
||||
if (expiration_enabled && filter_responses) {
|
||||
snprintf(sql_ptr, remaining,
|
||||
" AND NOT EXISTS (SELECT 1 FROM event_tags et_exp "
|
||||
"WHERE et_exp.event_id = events.id "
|
||||
"AND et_exp.tag_name = ? "
|
||||
"AND CAST(et_exp.tag_value AS INTEGER) <= ?)");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, "expiration");
|
||||
|
||||
char now_buf[32];
|
||||
snprintf(now_buf, sizeof(now_buf), "%lld", (long long)query_now);
|
||||
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, now_buf);
|
||||
}
|
||||
|
||||
// Handle kinds filter
|
||||
cJSON* kinds = cJSON_GetObjectItemCaseSensitive(filter, "kinds");
|
||||
if (kinds && cJSON_IsArray(kinds)) {
|
||||
@@ -1140,7 +1464,7 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
snprintf(sql_ptr, remaining, " AND kind IN (");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
|
||||
for (int k = 0; k < kind_count; k++) {
|
||||
cJSON* kind = cJSON_GetArrayItem(kinds, k);
|
||||
if (cJSON_IsNumber(kind)) {
|
||||
@@ -1159,7 +1483,7 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle authors filter
|
||||
cJSON* authors = cJSON_GetObjectItemCaseSensitive(filter, "authors");
|
||||
if (authors && cJSON_IsArray(authors)) {
|
||||
@@ -1205,8 +1529,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
if (ids && cJSON_IsArray(ids)) {
|
||||
int id_count = 0;
|
||||
// Count valid ids
|
||||
for (int i = 0; i < cJSON_GetArraySize(ids); i++) {
|
||||
cJSON* id = cJSON_GetArrayItem(ids, i);
|
||||
for (int j = 0; j < cJSON_GetArraySize(ids); j++) {
|
||||
cJSON* id = cJSON_GetArrayItem(ids, j);
|
||||
if (cJSON_IsString(id)) {
|
||||
id_count++;
|
||||
}
|
||||
@@ -1216,8 +1540,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
for (int i = 0; i < id_count; i++) {
|
||||
if (i > 0) {
|
||||
for (int j = 0; j < id_count; j++) {
|
||||
if (j > 0) {
|
||||
snprintf(sql_ptr, remaining, ",");
|
||||
sql_ptr++;
|
||||
remaining--;
|
||||
@@ -1231,8 +1555,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
// Add id values to bind params
|
||||
for (int i = 0; i < cJSON_GetArraySize(ids); i++) {
|
||||
cJSON* id = cJSON_GetArrayItem(ids, i);
|
||||
for (int j = 0; j < cJSON_GetArraySize(ids); j++) {
|
||||
cJSON* id = cJSON_GetArrayItem(ids, j);
|
||||
if (cJSON_IsString(id)) {
|
||||
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, cJSON_GetStringValue(id));
|
||||
}
|
||||
@@ -1251,8 +1575,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
if (cJSON_IsArray(filter_item)) {
|
||||
int tag_value_count = 0;
|
||||
// Count valid tag values
|
||||
for (int i = 0; i < cJSON_GetArraySize(filter_item); i++) {
|
||||
cJSON* tag_value = cJSON_GetArrayItem(filter_item, i);
|
||||
for (int j = 0; j < cJSON_GetArraySize(filter_item); j++) {
|
||||
cJSON* tag_value = cJSON_GetArrayItem(filter_item, j);
|
||||
if (cJSON_IsString(tag_value)) {
|
||||
tag_value_count++;
|
||||
}
|
||||
@@ -1263,8 +1587,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
for (int i = 0; i < tag_value_count; i++) {
|
||||
if (i > 0) {
|
||||
for (int j = 0; j < tag_value_count; j++) {
|
||||
if (j > 0) {
|
||||
snprintf(sql_ptr, remaining, ",");
|
||||
sql_ptr++;
|
||||
remaining--;
|
||||
@@ -1279,8 +1603,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
|
||||
// Add tag name and values to bind params
|
||||
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, tag_name);
|
||||
for (int i = 0; i < cJSON_GetArraySize(filter_item); i++) {
|
||||
cJSON* tag_value = cJSON_GetArrayItem(filter_item, i);
|
||||
for (int j = 0; j < cJSON_GetArraySize(filter_item); j++) {
|
||||
cJSON* tag_value = cJSON_GetArrayItem(filter_item, j);
|
||||
if (cJSON_IsString(tag_value)) {
|
||||
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, cJSON_GetStringValue(tag_value));
|
||||
}
|
||||
@@ -1299,12 +1623,12 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
// Escape single quotes in search term for SQL safety
|
||||
char escaped_search[256];
|
||||
size_t escaped_len = 0;
|
||||
for (size_t i = 0; search_term[i] && escaped_len < sizeof(escaped_search) - 1; i++) {
|
||||
if (search_term[i] == '\'') {
|
||||
for (size_t j = 0; search_term[j] && escaped_len < sizeof(escaped_search) - 1; j++) {
|
||||
if (search_term[j] == '\'') {
|
||||
escaped_search[escaped_len++] = '\'';
|
||||
escaped_search[escaped_len++] = '\'';
|
||||
} else {
|
||||
escaped_search[escaped_len++] = search_term[i];
|
||||
escaped_search[escaped_len++] = search_term[j];
|
||||
}
|
||||
}
|
||||
escaped_search[escaped_len] = '\0';
|
||||
@@ -1325,7 +1649,7 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
|
||||
|
||||
// Handle until filter
|
||||
cJSON* until = cJSON_GetObjectItemCaseSensitive(filter, "until");
|
||||
if (until && cJSON_IsNumber(until)) {
|
||||
@@ -1333,12 +1657,12 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
}
|
||||
|
||||
|
||||
// Add ordering and limit
|
||||
snprintf(sql_ptr, remaining, " ORDER BY created_at DESC");
|
||||
sql_ptr += strlen(sql_ptr);
|
||||
remaining = sizeof(sql) - strlen(sql);
|
||||
|
||||
|
||||
// Handle limit filter
|
||||
cJSON* limit = cJSON_GetObjectItemCaseSensitive(filter, "limit");
|
||||
if (limit && cJSON_IsNumber(limit)) {
|
||||
@@ -1351,116 +1675,27 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
snprintf(sql_ptr, remaining, " LIMIT 500");
|
||||
}
|
||||
|
||||
// Start query timing
|
||||
struct timespec query_start, query_end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_start);
|
||||
|
||||
// Execute query through db thread pool helper and send events
|
||||
thread_pool_req_result_t* req_result = NULL;
|
||||
if (thread_pool_execute_req_sync(sql, (const char**)bind_params, bind_param_count, 5000, &req_result) != 0 || !req_result) {
|
||||
char error_msg[256];
|
||||
snprintf(error_msg, sizeof(error_msg), "Failed to execute subscription query: %s", db_last_error());
|
||||
DEBUG_ERROR(error_msg);
|
||||
|
||||
// Log the failed query so we can see what SQL was generated
|
||||
if (g_debug_level >= DEBUG_LEVEL_DEBUG) {
|
||||
time_t now = time(NULL);
|
||||
struct tm* tm_info = localtime(&now);
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
fprintf(stderr, "[%s] [QUERY_FAILED] type=REQ sub=%s ip=%s error=%s sql=%s\n",
|
||||
timestamp,
|
||||
sub_id,
|
||||
pss ? pss->client_ip : "N/A",
|
||||
db_last_error(),
|
||||
sql);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
// Submit async REQ query (results processed on lws thread via completion queue)
|
||||
if (submit_req_query_async(async_state, sql, (const char**)bind_params, bind_param_count) != 0) {
|
||||
DEBUG_ERROR("Failed to submit async REQ query for subscription %s", sub_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track query execution for abuse detection
|
||||
submitted_jobs++;
|
||||
if (pss) {
|
||||
pss->db_queries_executed++;
|
||||
}
|
||||
|
||||
// Cache config values outside the row loop (performance fix)
|
||||
int expiration_enabled = get_config_bool("expiration_enabled", 1);
|
||||
int filter_responses = get_config_bool("expiration_filter", 1);
|
||||
|
||||
int row_count = 0;
|
||||
for (int r = 0; r < req_result->row_count; r++) {
|
||||
const char* event_json_str = req_result->event_json_rows[r];
|
||||
row_count++;
|
||||
|
||||
// Track rows returned for abuse detection
|
||||
if (pss) {
|
||||
pss->db_rows_returned++;
|
||||
}
|
||||
|
||||
if (!event_json_str) {
|
||||
DEBUG_ERROR("Event has NULL event_json field");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse event JSON only for expiration check
|
||||
cJSON* event = cJSON_Parse(event_json_str);
|
||||
if (!event) {
|
||||
DEBUG_ERROR("Failed to parse event_json from database");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check expiration filtering (NIP-40) at application level
|
||||
// (expiration_enabled and filter_responses are cached outside the loop)
|
||||
if (expiration_enabled && filter_responses) {
|
||||
time_t current_time = time(NULL);
|
||||
if (is_event_expired(event, current_time)) {
|
||||
// Skip this expired event
|
||||
cJSON_Delete(event);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Build EVENT message using zero-copy path: allocate with LWS_PRE prefix,
|
||||
// write directly, transfer ownership to queue — no memcpy.
|
||||
// Format: ["EVENT","<sub_id>",<event_json>]
|
||||
size_t sub_id_len = strlen(sub_id);
|
||||
size_t event_json_len = strlen(event_json_str);
|
||||
size_t msg_len = 10 + sub_id_len + 3 + event_json_len + 1;
|
||||
|
||||
unsigned char* buf = malloc(LWS_PRE + msg_len + 1);
|
||||
if (buf) {
|
||||
char* msg_ptr = (char*)(buf + LWS_PRE);
|
||||
snprintf(msg_ptr, msg_len + 1, "[\"EVENT\",\"%s\",%s]", sub_id, event_json_str);
|
||||
size_t actual_len = strlen(msg_ptr);
|
||||
|
||||
// queue_message_take_ownership takes buf ownership — no memcpy, no free needed here
|
||||
if (queue_message_take_ownership(wsi, pss, buf, actual_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue EVENT message for sub=%s", sub_id);
|
||||
// buf already freed by queue_message_take_ownership on failure
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(event);
|
||||
events_sent++;
|
||||
}
|
||||
|
||||
thread_pool_free_req_result(req_result);
|
||||
|
||||
// Stop query timing and log
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_end);
|
||||
long elapsed_us = (query_end.tv_sec - query_start.tv_sec) * 1000000L +
|
||||
(query_end.tv_nsec - query_start.tv_nsec) / 1000L;
|
||||
|
||||
log_query_execution("REQ", sub_id, pss ? pss->client_ip : NULL,
|
||||
sql, elapsed_us, row_count);
|
||||
}
|
||||
|
||||
// Cleanup bind params
|
||||
free_bind_params(bind_params, bind_param_count);
|
||||
|
||||
return events_sent;
|
||||
if (submitted_jobs == 0) {
|
||||
req_async_state_free(async_state);
|
||||
return events_sent;
|
||||
}
|
||||
|
||||
return HANDLE_REQ_ASYNC_PENDING;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -2042,7 +2277,7 @@ int main(int argc, char* argv[]) {
|
||||
ip_ban_load_from_db();
|
||||
|
||||
// Optional thread pool scaffold initialization (execution wiring is future work)
|
||||
int thread_pool_enabled = get_config_bool("thread_pool_enabled", 0);
|
||||
int thread_pool_enabled = get_config_bool("thread_pool_enabled", 1);
|
||||
int thread_pool_initialized = 0;
|
||||
if (thread_pool_enabled) {
|
||||
thread_pool_config_t tp_cfg;
|
||||
|
||||
+8
-2
@@ -13,8 +13,8 @@
|
||||
// Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||
#define CRELAY_VERSION_MAJOR 2
|
||||
#define CRELAY_VERSION_MINOR 1
|
||||
#define CRELAY_VERSION_PATCH 1
|
||||
#define CRELAY_VERSION "v2.1.1"
|
||||
#define CRELAY_VERSION_PATCH 2
|
||||
#define CRELAY_VERSION "v2.1.2"
|
||||
|
||||
// Relay metadata (authoritative source for NIP-11 information)
|
||||
#define RELAY_NAME "C-Relay"
|
||||
@@ -28,4 +28,10 @@
|
||||
#define POSTING_POLICY ""
|
||||
#define PAYMENTS_URL ""
|
||||
|
||||
// Async REQ handler status used by websocket callback to defer EOSE until worker completion
|
||||
#define HANDLE_REQ_ASYNC_PENDING (-2)
|
||||
|
||||
// Drains completed async REQ jobs and queues EVENT/EOSE on the lws service thread.
|
||||
void process_req_async_completions(void);
|
||||
|
||||
#endif /* MAIN_H */
|
||||
|
||||
+68
-2
@@ -4,7 +4,10 @@
|
||||
#include "debug.h"
|
||||
#include "db_ops.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -37,6 +40,8 @@ typedef struct {
|
||||
thread_pool_wake_loop_cb wake_loop_cb;
|
||||
void* wake_loop_ctx;
|
||||
|
||||
char db_path[512];
|
||||
|
||||
pthread_mutex_t state_mutex;
|
||||
} thread_pool_state_t;
|
||||
|
||||
@@ -193,6 +198,26 @@ static void wake_event_loop(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static sqlite3* open_worker_connection(void) {
|
||||
if (g_pool.db_path[0] == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sqlite3* db = NULL;
|
||||
int rc = sqlite3_open_v2(g_pool.db_path, &db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (db) {
|
||||
sqlite3_close(db);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
|
||||
sqlite3_busy_timeout(db, 5000);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
static void complete_job_with_result(thread_pool_job_node_t* node,
|
||||
thread_pool_status_t status,
|
||||
const char* message,
|
||||
@@ -374,24 +399,54 @@ static void execute_write_job(thread_pool_job_node_t* node) {
|
||||
}
|
||||
|
||||
static void* reader_worker_main(void* arg) {
|
||||
(void)arg;
|
||||
int reader_index = (int)(intptr_t)arg;
|
||||
|
||||
char thread_name[16];
|
||||
snprintf(thread_name, sizeof(thread_name), "db-read-%d", reader_index);
|
||||
pthread_setname_np(pthread_self(), thread_name);
|
||||
|
||||
sqlite3* worker_db = open_worker_connection();
|
||||
if (!worker_db) {
|
||||
DEBUG_ERROR("Reader worker failed to open SQLite connection");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db_set_thread_connection(worker_db);
|
||||
|
||||
while (g_pool.running) {
|
||||
thread_pool_job_node_t* node = queue_pop(&g_pool.read_q);
|
||||
if (!node) break;
|
||||
|
||||
execute_read_job(node);
|
||||
}
|
||||
|
||||
db_clear_thread_connection();
|
||||
sqlite3_close(worker_db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* writer_worker_main(void* arg) {
|
||||
(void)arg;
|
||||
|
||||
pthread_setname_np(pthread_self(), "db-write");
|
||||
|
||||
sqlite3* worker_db = open_worker_connection();
|
||||
if (!worker_db) {
|
||||
DEBUG_ERROR("Writer worker failed to open SQLite connection");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db_set_thread_connection(worker_db);
|
||||
|
||||
while (g_pool.running) {
|
||||
thread_pool_job_node_t* node = queue_pop(&g_pool.write_q);
|
||||
if (!node) break;
|
||||
|
||||
execute_write_job(node);
|
||||
}
|
||||
|
||||
db_clear_thread_connection();
|
||||
sqlite3_close(worker_db);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -417,10 +472,21 @@ int thread_pool_init(const thread_pool_config_t* config) {
|
||||
g_pool.next_job_id = 1;
|
||||
g_pool.wake_loop_cb = config->wake_loop_cb;
|
||||
g_pool.wake_loop_ctx = config->wake_loop_ctx;
|
||||
|
||||
const char* db_path = (config->db_path && config->db_path[0] != '\0') ? config->db_path : db_get_database_path();
|
||||
if (!db_path || db_path[0] == '\0') {
|
||||
free(g_pool.readers);
|
||||
g_pool.readers = NULL;
|
||||
pthread_mutex_unlock(&g_pool.state_mutex);
|
||||
return -1;
|
||||
}
|
||||
strncpy(g_pool.db_path, db_path, sizeof(g_pool.db_path) - 1);
|
||||
g_pool.db_path[sizeof(g_pool.db_path) - 1] = '\0';
|
||||
|
||||
g_pool.running = 1;
|
||||
|
||||
for (int i = 0; i < g_pool.reader_count; i++) {
|
||||
if (pthread_create(&g_pool.readers[i], NULL, reader_worker_main, NULL) != 0) {
|
||||
if (pthread_create(&g_pool.readers[i], NULL, reader_worker_main, (void*)(intptr_t)(i + 1)) != 0) {
|
||||
g_pool.running = 0;
|
||||
pthread_cond_broadcast(&g_pool.read_q.cond);
|
||||
pthread_cond_broadcast(&g_pool.write_q.cond);
|
||||
|
||||
+53
-42
@@ -33,6 +33,8 @@
|
||||
#include "thread_pool.h" // Thread pool scaffold
|
||||
#include "db_ops.h" // DB abstraction wrappers
|
||||
|
||||
#include "main.h" // Async REQ completion integration
|
||||
|
||||
// Forward declarations for logging functions
|
||||
|
||||
// Forward declarations for configuration functions
|
||||
@@ -1286,38 +1288,41 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
DEBUG_TRACE("REQ filters validated successfully");
|
||||
|
||||
DEBUG_TRACE("About to call handle_req_message for subscription %s", subscription_id);
|
||||
handle_req_message(subscription_id, filters, wsi, pss);
|
||||
int req_result = handle_req_message(subscription_id, filters, wsi, pss);
|
||||
DEBUG_TRACE("handle_req_message completed for subscription %s", subscription_id);
|
||||
|
||||
// Clean up the filters array we created
|
||||
cJSON_Delete(filters);
|
||||
|
||||
DEBUG_LOG("REQ subscription %s processed, sending EOSE", subscription_id);
|
||||
// Async path will send EOSE from completion handler.
|
||||
if (req_result != HANDLE_REQ_ASYNC_PENDING) {
|
||||
DEBUG_LOG("REQ subscription %s processed, sending EOSE", subscription_id);
|
||||
|
||||
// Send EOSE (End of Stored Events)
|
||||
cJSON* eose_response = cJSON_CreateArray();
|
||||
if (eose_response) {
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString("EOSE"));
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString(subscription_id));
|
||||
// Send EOSE (End of Stored Events)
|
||||
cJSON* eose_response = cJSON_CreateArray();
|
||||
if (eose_response) {
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString("EOSE"));
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString(subscription_id));
|
||||
|
||||
char *eose_str = cJSON_Print(eose_response);
|
||||
if (eose_str) {
|
||||
size_t eose_len = strlen(eose_str);
|
||||
char *eose_str = cJSON_Print(eose_response);
|
||||
if (eose_str) {
|
||||
size_t eose_len = strlen(eose_str);
|
||||
|
||||
// DEBUG: Log WebSocket frame details before sending
|
||||
DEBUG_TRACE("WS_FRAME_SEND: type=EOSE len=%zu data=%.100s%s",
|
||||
eose_len,
|
||||
eose_str,
|
||||
eose_len > 100 ? "..." : "");
|
||||
// DEBUG: Log WebSocket frame details before sending
|
||||
DEBUG_TRACE("WS_FRAME_SEND: type=EOSE len=%zu data=%.100s%s",
|
||||
eose_len,
|
||||
eose_str,
|
||||
eose_len > 100 ? "..." : "");
|
||||
|
||||
// Queue message for proper libwebsockets pattern
|
||||
if (queue_message(wsi, pss, eose_str, eose_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue EOSE message");
|
||||
// Queue message for proper libwebsockets pattern
|
||||
if (queue_message(wsi, pss, eose_str, eose_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue EOSE message");
|
||||
}
|
||||
|
||||
free(eose_str);
|
||||
}
|
||||
|
||||
free(eose_str);
|
||||
cJSON_Delete(eose_response);
|
||||
}
|
||||
cJSON_Delete(eose_response);
|
||||
}
|
||||
} else {
|
||||
send_notice_message(wsi, pss, "error: missing or invalid subscription ID in REQ");
|
||||
@@ -2058,38 +2063,41 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
||||
DEBUG_TRACE("REQ filters validated successfully");
|
||||
|
||||
DEBUG_TRACE("About to call handle_req_message for subscription %s", subscription_id);
|
||||
handle_req_message(subscription_id, filters, wsi, pss);
|
||||
int req_result = handle_req_message(subscription_id, filters, wsi, pss);
|
||||
DEBUG_TRACE("handle_req_message completed for subscription %s", subscription_id);
|
||||
|
||||
// Clean up the filters array we created
|
||||
cJSON_Delete(filters);
|
||||
|
||||
DEBUG_LOG("REQ subscription %s processed, sending EOSE", subscription_id);
|
||||
// Async path will send EOSE from completion handler.
|
||||
if (req_result != HANDLE_REQ_ASYNC_PENDING) {
|
||||
DEBUG_LOG("REQ subscription %s processed, sending EOSE", subscription_id);
|
||||
|
||||
// Send EOSE (End of Stored Events)
|
||||
cJSON* eose_response = cJSON_CreateArray();
|
||||
if (eose_response) {
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString("EOSE"));
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString(subscription_id));
|
||||
// Send EOSE (End of Stored Events)
|
||||
cJSON* eose_response = cJSON_CreateArray();
|
||||
if (eose_response) {
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString("EOSE"));
|
||||
cJSON_AddItemToArray(eose_response, cJSON_CreateString(subscription_id));
|
||||
|
||||
char *eose_str = cJSON_Print(eose_response);
|
||||
if (eose_str) {
|
||||
size_t eose_len = strlen(eose_str);
|
||||
char *eose_str = cJSON_Print(eose_response);
|
||||
if (eose_str) {
|
||||
size_t eose_len = strlen(eose_str);
|
||||
|
||||
// DEBUG: Log WebSocket frame details before sending
|
||||
DEBUG_TRACE("WS_FRAME_SEND: type=EOSE len=%zu data=%.100s%s",
|
||||
eose_len,
|
||||
eose_str,
|
||||
eose_len > 100 ? "..." : "");
|
||||
// DEBUG: Log WebSocket frame details before sending
|
||||
DEBUG_TRACE("WS_FRAME_SEND: type=EOSE len=%zu data=%.100s%s",
|
||||
eose_len,
|
||||
eose_str,
|
||||
eose_len > 100 ? "..." : "");
|
||||
|
||||
// Queue message for proper libwebsockets pattern
|
||||
if (queue_message(wsi, pss, eose_str, eose_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue EOSE message");
|
||||
// Queue message for proper libwebsockets pattern
|
||||
if (queue_message(wsi, pss, eose_str, eose_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue EOSE message");
|
||||
}
|
||||
|
||||
free(eose_str);
|
||||
}
|
||||
|
||||
free(eose_str);
|
||||
cJSON_Delete(eose_response);
|
||||
}
|
||||
cJSON_Delete(eose_response);
|
||||
}
|
||||
} else {
|
||||
send_notice_message(wsi, pss, "error: missing or invalid subscription ID in REQ");
|
||||
@@ -2647,6 +2655,9 @@ int start_websocket_relay(int port_override, int strict_port) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Drain completed async REQ jobs and emit EVENT/EOSE on service thread.
|
||||
process_req_async_completions();
|
||||
|
||||
// Check if it's time to post status update
|
||||
time_t current_time = time(NULL);
|
||||
int status_post_hours = get_config_int("kind_1_status_posts_hours", 0);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
2026-04-01 09:10:57 - ==========================================
|
||||
2026-04-01 09:10:57 - C-Relay Comprehensive Test Suite Runner
|
||||
2026-04-01 09:10:57 - ==========================================
|
||||
2026-04-01 09:10:57 - Relay URL: ws://127.0.0.1:8888
|
||||
2026-04-01 09:10:57 - Log file: test_results_20260401_091057.log
|
||||
2026-04-01 09:10:57 - Report file: test_report_20260401_091057.html
|
||||
2026-04-01 09:10:57 -
|
||||
2026-04-01 09:10:57 - Checking relay status at ws://127.0.0.1:8888...
|
||||
2026-04-01 09:10:57 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
|
||||
2026-04-01 09:10:57 -
|
||||
2026-04-01 09:10:57 - Starting comprehensive test execution...
|
||||
2026-04-01 09:10:57 -
|
||||
2026-04-01 09:10:57 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
|
||||
2026-04-01 09:10:57 - ==========================================
|
||||
2026-04-01 09:10:57 - Running Test Suite: SQL Injection Tests
|
||||
2026-04-01 09:10:57 - Description: Comprehensive SQL injection vulnerability testing
|
||||
2026-04-01 09:10:57 - ==========================================
|
||||
2026-04-01 09:10:57 - \033[0;31mERROR: Test script sql_injection_tests.sh not found\033[0m
|
||||
@@ -1,18 +0,0 @@
|
||||
2026-04-01 09:32:38 - ==========================================
|
||||
2026-04-01 09:32:38 - C-Relay Comprehensive Test Suite Runner
|
||||
2026-04-01 09:32:38 - ==========================================
|
||||
2026-04-01 09:32:38 - Relay URL: ws://127.0.0.1:8888
|
||||
2026-04-01 09:32:38 - Log file: test_results_20260401_093238.log
|
||||
2026-04-01 09:32:38 - Report file: test_report_20260401_093238.html
|
||||
2026-04-01 09:32:38 -
|
||||
2026-04-01 09:32:38 - Checking relay status at ws://127.0.0.1:8888...
|
||||
2026-04-01 09:32:38 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
|
||||
2026-04-01 09:32:38 -
|
||||
2026-04-01 09:32:38 - Starting comprehensive test execution...
|
||||
2026-04-01 09:32:38 -
|
||||
2026-04-01 09:32:38 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
|
||||
2026-04-01 09:32:38 - ==========================================
|
||||
2026-04-01 09:32:38 - Running Test Suite: SQL Injection Tests
|
||||
2026-04-01 09:32:38 - Description: Comprehensive SQL injection vulnerability testing
|
||||
2026-04-01 09:32:38 - ==========================================
|
||||
2026-04-01 09:32:38 - \033[0;31mERROR: Test script sql_injection_tests.sh not found\033[0m
|
||||
@@ -0,0 +1,18 @@
|
||||
2026-04-01 09:57:22 - ==========================================
|
||||
2026-04-01 09:57:22 - C-Relay Comprehensive Test Suite Runner
|
||||
2026-04-01 09:57:22 - ==========================================
|
||||
2026-04-01 09:57:22 - Relay URL: ws://127.0.0.1:8888
|
||||
2026-04-01 09:57:22 - Log file: test_results_20260401_095722.log
|
||||
2026-04-01 09:57:22 - Report file: test_report_20260401_095722.html
|
||||
2026-04-01 09:57:22 -
|
||||
2026-04-01 09:57:22 - Checking relay status at ws://127.0.0.1:8888...
|
||||
2026-04-01 09:57:22 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
|
||||
2026-04-01 09:57:22 -
|
||||
2026-04-01 09:57:22 - Starting comprehensive test execution...
|
||||
2026-04-01 09:57:22 -
|
||||
2026-04-01 09:57:22 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
|
||||
2026-04-01 09:57:22 - ==========================================
|
||||
2026-04-01 09:57:22 - Running Test Suite: SQL Injection Tests
|
||||
2026-04-01 09:57:22 - Description: Comprehensive SQL injection vulnerability testing
|
||||
2026-04-01 09:57:22 - ==========================================
|
||||
2026-04-01 09:57:22 - \033[0;31mERROR: Test script sql_injection_tests.sh not found\033[0m
|
||||
+26
-26
@@ -1,5 +1,5 @@
|
||||
=== NIP-42 Authentication Test Started ===
|
||||
2026-04-01 05:46:11 - Starting NIP-42 authentication tests
|
||||
2026-04-01 10:00:44 - Starting NIP-42 authentication tests
|
||||
[34m[1m[INFO][0m === Starting NIP-42 Authentication Tests ===
|
||||
[34m[1m[INFO][0m Checking dependencies...
|
||||
[33m[1m[WARNING][0m wscat not found. Some manual WebSocket tests will be skipped
|
||||
@@ -7,7 +7,7 @@
|
||||
[32m[1m[SUCCESS][0m Dependencies check complete
|
||||
[34m[1m[INFO][0m Test 1: Checking NIP-42 support in relay info
|
||||
[32m[1m[SUCCESS][0m NIP-42 is advertised in supported NIPs
|
||||
2026-04-01 05:46:11 - Supported NIPs: 1,2,4,9,11,12,13,15,16,20,22,33,40,42,50,70
|
||||
2026-04-01 10:00:44 - Supported NIPs: 1,2,4,9,11,12,13,15,16,20,22,33,40,42,50,70
|
||||
[34m[1m[INFO][0m Test 2: Testing AUTH challenge generation
|
||||
[33m[1m[WARNING][0m Could not extract admin private key from relay.log - using manual test approach
|
||||
[34m[1m[INFO][0m Manual test: Connect to relay and send an event without auth to trigger challenge
|
||||
@@ -15,8 +15,8 @@
|
||||
[34m[1m[INFO][0m Generated test keypair: test_pubkey
|
||||
[34m[1m[INFO][0m Attempting to publish event without authentication...
|
||||
[34m[1m[INFO][0m Publishing test event to relay...
|
||||
2026-04-01 05:46:12 - Event publish result: connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"334754e42c2bd54bdf733fb2c9a28c1e0f33d2275ca2c70f26062187793b37fe","pubkey":"40d9f28055baeba6a9bcac1942a17370add58a95a46985f66c5c14d485573983","created_at":1775036772,"tags":[],"content":"NIP-42 test event - should require auth","sig":"6e81c7335d34a770c72566250ac363aa5a126383a1efe2effe5b4b7e84bf600c4d9531b26e9be188fe884e9bd32aa384d49739815c7fba4594174277b90cbf7d"}
|
||||
2026-04-01 10:00:45 - Event publish result: connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"28174ed72f9ef5484a6596db6c6be181d2ed10e46d8c22a9f7a267becbc9a47a","pubkey":"b5f9adc3b362c0e81569a46648a3996708d6b47754d36c617683f18e1f1daf6b","created_at":1775052045,"tags":[],"content":"NIP-42 test event - should require auth","sig":"0041347b233a21d6542a263634f52f8ca7e3b5ecf07bc37c86f390f1a7ab0bc2051306b14aa70edff020bd7311b259d03c2c35a035b285f9e19051a924546666"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
[32m[1m[SUCCESS][0m Relay requested authentication as expected
|
||||
[34m[1m[INFO][0m Test 4: Testing WebSocket AUTH message handling
|
||||
@@ -26,50 +26,50 @@ publishing to ws://localhost:8888... success.
|
||||
[33m[1m[WARNING][0m Could not retrieve configuration events
|
||||
[34m[1m[INFO][0m Test 6: Testing NIP-42 performance and stability
|
||||
[34m[1m[INFO][0m Testing multiple authentication attempts...
|
||||
2026-04-01 05:46:13 - Attempt 1: .187311847s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"470a74d32e1b739e6bfd275c0d03ab1508fb5908c0fa01523e4a3bdce6a95db3","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036773,"tags":[],"content":"Performance test event 1","sig":"0c30a125cb67d336413f006a5dfa422d788e177254e496b966854bf45183238d23f6575dba37dec6fa46b6f6b906a25afafcbbcaa7d0c15add89dd7fd1a80c22"}
|
||||
2026-04-01 10:00:46 - Attempt 1: .195528002s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"72526c57e50d721bfcdcca23856f2c5d3021c3100f0121538d9d829042e9b8b5","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052046,"tags":[],"content":"Performance test event 1","sig":"1a10db86a39aaf47bda6d0cca5f888691bae32bfd85df8c178dddbf24861503a491bfe4da8d66dfe3e5e0afec7bf8644e7eb69c9f051c601673ae13284f9ab31"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
2026-04-01 05:46:13 - Attempt 2: .185565503s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"e4f9db77d30d8e71ef01b16dcb1179d03462aaf368aae30120898f3758a9d0b9","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036773,"tags":[],"content":"Performance test event 2","sig":"24a8f38c2cb230d95120e60a246a1a03ac17d4d195d25f2ae0b2f674c3383287eca53cd7fc61bc3cdc58433a3d2c2108558f4f1a98dcfc02a97b4ec8c537cdde"}
|
||||
2026-04-01 10:00:46 - Attempt 2: .185198180s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"84eeb907205806bb88fc353bb83f1bf0149f3c93c18695306d8480d36752878b","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052046,"tags":[],"content":"Performance test event 2","sig":"e568dbca3113966f61e69eff05576d296fac42b5ff2ab7341778acc02cf6e9440b59bf651a8a2995d89180a62ec20ffc1143e211cf2960c5abaa358a82992ac6"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
2026-04-01 05:46:14 - Attempt 3: .185110183s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"4a85e466756452e90358fe5d98fa41bda93b6bc77cb079bb1616ca8873f4fca1","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036774,"tags":[],"content":"Performance test event 3","sig":"72ab716ff3b4786309fd4cef27ae8cd9df1e3c1746b5e49ba45c070cafcfb72a5b7ba045e7d798f23b5d82989526b4d3c818509f0eec639c8ad265be85a4728e"}
|
||||
2026-04-01 10:00:47 - Attempt 3: .197159540s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"0874716ba0b2a845d379e703226513f90db6228ce958369f4dce5aff3df64c5a","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052046,"tags":[],"content":"Performance test event 3","sig":"e74e885eb464998bddf655c24c95a18d7299908c63fb82a47f8d14af65992f90e73794a9f289b550bc1dec1c0298112d2b25941be7e26e3f17dd66b4c5dc742e"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
2026-04-01 05:46:14 - Attempt 4: .185725746s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"4c05b6359da88f85c0aa4c94a296a465f71bddaa10d849ed0923f972ac0628a9","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036774,"tags":[],"content":"Performance test event 4","sig":"3b0c8bd1f3d965a259a0ab228ea4c47defef3ad563ebce860ec65ab0ee341c5305c0988ac88651664d3090ce2ff282a0fe4913231448158c423a74b6e3b2a7a9"}
|
||||
2026-04-01 10:00:47 - Attempt 4: .202520319s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"5614d1b795cd028e096720418bb4dbc64870d442c6295b9bb2931564a332099a","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052047,"tags":[],"content":"Performance test event 4","sig":"1c0645a791ab9df573b152b782884bcb5ec2a18b8f4e1c18d7dc2175977b7d1d572674169ea1d2b3e3fa8d228de90f6cc281d56a3ef471ba4f9510e920ed586e"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
2026-04-01 05:46:15 - Attempt 5: .184467365s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"0902e868c5e7909f0143fde7738d5e8f4c42ea0ced2c69a404cfeb6cd686ceaa","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036774,"tags":[],"content":"Performance test event 5","sig":"a0754e774e5bc3514dc4cf04ec51cab077413251cfce480e80badccc7660bfe8d1eac87d43c6e332d936cc171d24e8318e1ef5df7425a8218e01af661f291c47"}
|
||||
2026-04-01 10:00:47 - Attempt 5: .222530325s - connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"9139f1eb39c31c43cb9f02e60a0f75a0f99f6c7d9693a69a225c8b3e530f774a","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052047,"tags":[],"content":"Performance test event 5","sig":"bd245a7629c33619c0ed426f631e0c2413adb4488c281bea09886ceb6407b40e247d42f82e374e2f53ee5281f0d8361661a4c613c6414c59d09e5bb23eeeb02d"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
[32m[1m[SUCCESS][0m Performance test completed: 5/5 successful responses
|
||||
[34m[1m[INFO][0m Test 7: Testing kind-specific NIP-42 authentication requirements
|
||||
[34m[1m[INFO][0m Generated test keypair for kind-specific tests: test_pubkey
|
||||
[34m[1m[INFO][0m Testing kind 1 event (regular note) - should work without authentication...
|
||||
2026-04-01 05:46:15 - Kind 1 event result: connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"a1d9545c4ac16c2ab1fae4d5d77cabd368d765533b6f6d1f4dbb3c7859696a5e","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036775,"tags":[],"content":"Regular note - should not require auth","sig":"520796b21690bf03b20da28f30ffc84c9c198d0a908c07540f232f789c7a0327861e1f580d499be15b244bdf66c5c4b1cdf621bfdc5f60b1313d7f40036ffab8"}
|
||||
2026-04-01 10:00:48 - Kind 1 event result: connecting to localhost:8888... ok.
|
||||
{"kind":1,"id":"def386c66576a91f144a7321e9520a3ed46f9fecc56d9e79d32a162f249cace9","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052048,"tags":[],"content":"Regular note - should not require auth","sig":"7ee8fbf45e96d040df1a688e9bf028faaa49b81b26eeae6452b3a32cd9fd941c27b41734f14268d3deef14dbb19e514d29789211fffb27bfa9b73d20cddcc83e"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
[32m[1m[SUCCESS][0m Kind 1 event accepted without authentication (correct behavior)
|
||||
[34m[1m[INFO][0m Testing kind 4 event (direct message) - should require authentication...
|
||||
2026-04-01 05:46:25 - Kind 4 event result: connecting to localhost:8888... ok.
|
||||
{"kind":4,"id":"c047f5a9fb6ff46828d0bb862e4bcc32120e50fc7804d651485e2cdbddaf993a","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036775,"tags":[["p,test_pubkey"]],"content":"This is a direct message - should require auth","sig":"a7a027c302faf75e269d866a1b0137f2fbc02643d76435dc68707d2ba6560af95df709787f4a1a1bc7c1b9951615ad081a97cd7f694da94d5606d17100f0b0dc"}
|
||||
2026-04-01 10:00:58 - Kind 4 event result: connecting to localhost:8888... ok.
|
||||
{"kind":4,"id":"a2f2d176a835d17a0c9229c0ce3b1ba61a5eb81308e4a3b3446634f468721238","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052048,"tags":[["p,test_pubkey"]],"content":"This is a direct message - should require auth","sig":"441f9caba6ebcec6b6c55d13ffd9571f3fe441a54ddd0692f2868429a5087378d791c97289b8b990560dcf40f5c66e86ba6b41f1de78220a5f389c1f119bb887"}
|
||||
publishing to ws://localhost:8888...
|
||||
[32m[1m[SUCCESS][0m Kind 4 event requested authentication (correct behavior for DMs)
|
||||
[34m[1m[INFO][0m Testing kind 14 event (chat message) - should require authentication...
|
||||
2026-04-01 05:46:36 - Kind 14 event result: connecting to localhost:8888... ok.
|
||||
{"kind":14,"id":"73e50afdaefdbb01ed021f8c888a87b00aa59e4d68c124101353fabcdfa59ae1","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036786,"tags":[["p,test_pubkey"]],"content":"Chat message - should require auth","sig":"ec2c713e2add876856e6cb0d1d9790c43525c5139a0fe06fbee71898f1042173dc3af9187e6fbccfef7406c1af2f218631a1cdcb7ad73e34553cdf3aecc4f209"}
|
||||
2026-04-01 10:01:09 - Kind 14 event result: connecting to localhost:8888... ok.
|
||||
{"kind":14,"id":"e234a6faa63f95cd24f8596fbfac573730533bb0abcde47b4d341ff0acace657","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052059,"tags":[["p,test_pubkey"]],"content":"Chat message - should require auth","sig":"fd6ba0ddaa7e2379aa49c5cda6599d0a38264799b228a8e907b948bef645080603f7f910c587f6cfb1ef1bac946c47dc681fdab6abd248376cd552bcd535ef9e"}
|
||||
publishing to ws://localhost:8888...
|
||||
[32m[1m[SUCCESS][0m Kind 14 event requested authentication (correct behavior for DMs)
|
||||
[34m[1m[INFO][0m Testing other event kinds - should work without authentication...
|
||||
2026-04-01 05:46:36 - Kind 0 event result: connecting to localhost:8888... ok.
|
||||
{"kind":0,"id":"51fe1630721cfa3472ba7c3af911a3164c788a48c6a678179c62be48ac3d1f1c","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036796,"tags":[],"content":"Test event kind 0 - should not require auth","sig":"d72d827742fdc86daf1389c8dad40efc56c22b9744ad23547ea0b0a37598e5c18ccdac20b9ef2e6adf46d7683bff19f2cbc11fb00a252903efc1076c8f613b38"}
|
||||
2026-04-01 10:01:09 - Kind 0 event result: connecting to localhost:8888... ok.
|
||||
{"kind":0,"id":"d87b69e8a2c8f6d98229b55cde3f0fe1b70c6cafd080e4462209849338eafc76","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052069,"tags":[],"content":"Test event kind 0 - should not require auth","sig":"e3222bd4882c881d039e6790a6a8d9f8e20a71d0f9d5089e98604acaa4feff7c1b1b2d9b4171d640da3bba955408a9391a0fd52334cc4a4165fa932e5163f9dd"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
[32m[1m[SUCCESS][0m Kind 0 event accepted without authentication (correct)
|
||||
2026-04-01 05:46:36 - Kind 3 event result: connecting to localhost:8888... ok.
|
||||
{"kind":3,"id":"bf4155eb8b54bdd73a0b64365666caf886433634693cc3c4ab1d586187fc6bae","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036796,"tags":[],"content":"Test event kind 3 - should not require auth","sig":"d27f654ccc9f95e76aacafcce7d42528545fa90b77d422a17301de6931883d8c00a0a84af56ab5d37a0f425140dbfc227458557f9050597beec42cacd0a15864"}
|
||||
2026-04-01 10:01:09 - Kind 3 event result: connecting to localhost:8888... ok.
|
||||
{"kind":3,"id":"ac0d45d7db2818bb5e674d4760711349928cf3d5dc8f1fc3717d9e7274e26fb0","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052069,"tags":[],"content":"Test event kind 3 - should not require auth","sig":"b65b317c81bc733ee2ff5921430b34d722e8d177147c5d1cddf14036573223582a3b7232810fc13169eb7a61ab3b99cd37ce13991673fce142d1ea10c702305e"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
[32m[1m[SUCCESS][0m Kind 3 event accepted without authentication (correct)
|
||||
2026-04-01 05:46:37 - Kind 7 event result: connecting to localhost:8888... ok.
|
||||
{"kind":7,"id":"658e0caf1c4b52062363849d55312070005124e8a10202df285078222e523132","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036797,"tags":[],"content":"Test event kind 7 - should not require auth","sig":"50361b3aa6b85747befe20e50e317d8f905128930cec0263494434ec9029089d880ff23cdf071c5b63b8417eff216658f1229c0f0dc5174a9a16a6d5fe38fd4d"}
|
||||
2026-04-01 10:01:10 - Kind 7 event result: connecting to localhost:8888... ok.
|
||||
{"kind":7,"id":"ad7e77948a4742abfca2d63c5bb748f3db3940c85841f521b3b7d634bf09955a","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052070,"tags":[],"content":"Test event kind 7 - should not require auth","sig":"8a5e5a03956cec04d56934314a0876fccbd221546432aaccdbc249b5f9cedc221219abf6e2856b98675de205afaa0e91ea642933ec6724576a40db43d17aa758"}
|
||||
publishing to ws://localhost:8888... success.
|
||||
[32m[1m[SUCCESS][0m Kind 7 event accepted without authentication (correct)
|
||||
[34m[1m[INFO][0m Kind-specific authentication test completed
|
||||
|
||||
@@ -0,0 +1,733 @@
|
||||
2026-04-01 09:57:29 - ==========================================
|
||||
2026-04-01 09:57:30 - C-Relay Comprehensive Test Suite Runner
|
||||
2026-04-01 09:57:30 - ==========================================
|
||||
2026-04-01 09:57:30 - Relay URL: ws://127.0.0.1:8888
|
||||
2026-04-01 09:57:30 - Log file: test_results_20260401_095729.log
|
||||
2026-04-01 09:57:30 - Report file: test_report_20260401_095729.html
|
||||
2026-04-01 09:57:30 -
|
||||
2026-04-01 09:57:30 - Checking relay status at ws://127.0.0.1:8888...
|
||||
2026-04-01 09:57:30 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
|
||||
2026-04-01 09:57:30 -
|
||||
2026-04-01 09:57:30 - Starting comprehensive test execution...
|
||||
2026-04-01 09:57:30 -
|
||||
2026-04-01 09:57:30 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
|
||||
2026-04-01 09:57:30 - ==========================================
|
||||
2026-04-01 09:57:30 - Running Test Suite: SQL Injection Tests
|
||||
2026-04-01 09:57:30 - Description: Comprehensive SQL injection vulnerability testing
|
||||
2026-04-01 09:57:30 - ==========================================
|
||||
==========================================
|
||||
C-Relay SQL Injection Test Suite
|
||||
==========================================
|
||||
Testing against relay at ws://127.0.0.1:8888
|
||||
|
||||
=== Basic Connectivity Test ===
|
||||
Testing Basic connectivity... [0;32mPASSED[0m - Valid query works
|
||||
|
||||
=== Authors Filter SQL Injection Tests ===
|
||||
Testing Authors filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Authors filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== IDs Filter SQL Injection Tests ===
|
||||
Testing IDs filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing IDs filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Kinds Filter SQL Injection Tests ===
|
||||
Testing Kinds filter with string injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Kinds filter with negative value... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Kinds filter with very large value... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Search Filter SQL Injection Tests ===
|
||||
Testing Search filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing Search filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing Search filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing Search filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing Search filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Search filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Tag Filter SQL Injection Tests ===
|
||||
Testing #e tag filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #e tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #p tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #t tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #r tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: /*... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: */... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: /**/... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: #... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing #d tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
|
||||
=== Timestamp Filter SQL Injection Tests ===
|
||||
Testing Since parameter injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Until parameter injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Limit Parameter SQL Injection Tests ===
|
||||
Testing Limit parameter injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Limit with UNION... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Complex Multi-Filter SQL Injection Tests ===
|
||||
Testing Multi-filter with authors injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Multi-filter with search injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Multi-filter with tag injection... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
|
||||
=== COUNT Message SQL Injection Tests ===
|
||||
Testing COUNT with authors payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: '; DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 1' OR '1'='1... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing COUNT with authors payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 1; SELECT * FROM sqlite_master; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ' OR 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: admin'--... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 1' UNION SELECT password FROM users --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 1' AND SLEEP(5) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 1' AND 1=CAST((SELECT version()) AS INT) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ' UNION SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ' UNION SELECT 1,2,3 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ' UNION ALL SELECT NULL,NULL,NULL --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: '; SELECT * FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: '; DELETE FROM events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: /*... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: /*... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: */... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: */... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: /**/... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: /**/... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: #... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: #... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing COUNT with authors payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: 0x53514C5F494E4A454354494F4E... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing COUNT with authors payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: J1NSTCBJTkpFQ1RJT04gLS0=... [0;32mPASSED[0m - SQL injection blocked (query sanitized)
|
||||
Testing COUNT with authors payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: '))); DROP TABLE events; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ')) UNION SELECT NULL; --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ' AND 1=1 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ' AND 1=2 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: '; EXEC master..xp_cmdshell 'net user' --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with authors payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing COUNT with search payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Edge Case SQL Injection Tests ===
|
||||
Testing Empty string injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Null byte injection... [0;32mPASSED[0m - SQL injection blocked (silently rejected)
|
||||
Testing Unicode injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Very long injection payload... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Subscription ID SQL Injection Tests ===
|
||||
Testing Subscription ID injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
Testing Subscription ID with quotes... [0;32mPASSED[0m - SQL injection blocked (silently rejected)
|
||||
|
||||
=== CLOSE Message SQL Injection Tests ===
|
||||
Testing CLOSE with injection... [0;32mPASSED[0m - SQL injection blocked (rejected with error)
|
||||
|
||||
=== Test Results ===
|
||||
Total tests: 318
|
||||
Passed: [0;32m318[0m
|
||||
Failed: [0;31m0[0m
|
||||
[0;32m✓ All SQL injection tests passed![0m
|
||||
The relay appears to be protected against SQL injection attacks.
|
||||
2026-04-01 09:57:36 - \033[0;32m✓ SQL Injection Tests PASSED\033[0m (Duration: 6s)
|
||||
2026-04-01 09:57:36 - ==========================================
|
||||
2026-04-01 09:57:36 - Running Test Suite: Filter Validation Tests
|
||||
2026-04-01 09:57:36 - Description: Input validation for REQ and COUNT messages
|
||||
2026-04-01 09:57:36 - ==========================================
|
||||
=== C-Relay Filter Validation Tests ===
|
||||
Testing against relay at ws://127.0.0.1:8888
|
||||
|
||||
Testing Valid REQ message... [0;32mPASSED[0m
|
||||
Testing Valid COUNT message... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Filter Array Validation ===
|
||||
Testing Non-object filter... [0;32mPASSED[0m
|
||||
Testing Too many filters... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Authors Validation ===
|
||||
Testing Invalid author type... [0;32mPASSED[0m
|
||||
Testing Invalid author hex... [0;32mPASSED[0m
|
||||
Testing Too many authors... [0;32mPASSED[0m
|
||||
|
||||
=== Testing IDs Validation ===
|
||||
Testing Invalid ID type... [0;32mPASSED[0m
|
||||
Testing Invalid ID hex... [0;32mPASSED[0m
|
||||
Testing Too many IDs... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Kinds Validation ===
|
||||
Testing Invalid kind type... [0;32mPASSED[0m
|
||||
Testing Negative kind... [0;32mPASSED[0m
|
||||
Testing Too large kind... [0;32mPASSED[0m
|
||||
Testing Too many kinds... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Timestamp Validation ===
|
||||
Testing Invalid since type... [0;32mPASSED[0m
|
||||
Testing Negative since... [0;32mPASSED[0m
|
||||
Testing Invalid until type... [0;32mPASSED[0m
|
||||
Testing Negative until... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Limit Validation ===
|
||||
Testing Invalid limit type... [0;32mPASSED[0m
|
||||
Testing Negative limit... [0;32mPASSED[0m
|
||||
Testing Too large limit... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Search Validation ===
|
||||
Testing Invalid search type... [0;32mPASSED[0m
|
||||
Testing Search too long... [0;32mPASSED[0m
|
||||
Testing Search SQL injection... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Tag Filter Validation ===
|
||||
Testing Invalid tag filter type... [0;32mPASSED[0m
|
||||
Testing Too many tag values... [0;32mPASSED[0m
|
||||
Testing Tag value too long... [0;32mPASSED[0m
|
||||
|
||||
=== Testing Rate Limiting ===
|
||||
Testing rate limiting with malformed requests... [1;33mUNCERTAIN[0m - Rate limiting may not have triggered (this could be normal)
|
||||
|
||||
=== Test Results ===
|
||||
Total tests: 28
|
||||
Passed: [0;32m28[0m
|
||||
Failed: [0;31m0[0m
|
||||
[0;32mAll tests passed![0m
|
||||
2026-04-01 09:57:39 - \033[0;32m✓ Filter Validation Tests PASSED\033[0m (Duration: 3s)
|
||||
2026-04-01 09:57:39 - ==========================================
|
||||
2026-04-01 09:57:39 - Running Test Suite: Subscription Validation Tests
|
||||
2026-04-01 09:57:39 - Description: Subscription ID and message validation
|
||||
2026-04-01 09:57:39 - ==========================================
|
||||
Testing subscription ID validation fixes...
|
||||
Testing malformed subscription IDs...
|
||||
Empty ID test: Connection failed (expected)
|
||||
Long ID test: Connection failed (expected)
|
||||
Invalid chars test: Connection failed (expected)
|
||||
NULL ID test: Connection failed (expected)
|
||||
Valid ID test: Failed
|
||||
Testing CLOSE message validation...
|
||||
CLOSE empty ID test: Connection failed (expected)
|
||||
CLOSE valid ID test: Failed
|
||||
Subscription validation tests completed.
|
||||
2026-04-01 09:57:39 - \033[0;32m✓ Subscription Validation Tests PASSED\033[0m (Duration: 0s)
|
||||
2026-04-01 09:57:39 - ==========================================
|
||||
2026-04-01 09:57:39 - Running Test Suite: Memory Corruption Tests
|
||||
2026-04-01 09:57:39 - Description: Buffer overflow and memory safety testing
|
||||
2026-04-01 09:57:39 - ==========================================
|
||||
==========================================
|
||||
C-Relay Memory Corruption Test Suite
|
||||
==========================================
|
||||
Testing against relay at ws://127.0.0.1:8888
|
||||
Note: These tests may cause the relay to crash if vulnerabilities exist
|
||||
|
||||
=== Basic Connectivity Test ===
|
||||
Testing Basic connectivity... [0;32mPASSED[0m - No memory corruption detected
|
||||
|
||||
=== Subscription ID Memory Corruption Tests ===
|
||||
Testing Empty subscription ID... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Very long subscription ID (1KB)... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Very long subscription ID (10KB)... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Subscription ID with null bytes... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Subscription ID with special chars... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Unicode subscription ID... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Subscription ID with path traversal... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
|
||||
=== Filter Array Memory Corruption Tests ===
|
||||
Testing Too many filters (50)... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
|
||||
=== Concurrent Access Memory Tests ===
|
||||
Testing Concurrent subscription creation... ["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
|
||||
[0;32mPASSED[0m - Concurrent access handled safely
|
||||
Testing Concurrent CLOSE operations...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[0;32mPASSED[0m - Concurrent access handled safely
|
||||
|
||||
=== Malformed JSON Memory Tests ===
|
||||
Testing Unclosed JSON object... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Mismatched brackets... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Extra closing brackets... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Null bytes in JSON... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
|
||||
=== Large Message Memory Tests ===
|
||||
Testing Very large filter array... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
Testing Very long search term... [1;33mUNCERTAIN[0m - Expected error but got normal response
|
||||
|
||||
=== Test Results ===
|
||||
Total tests: 17
|
||||
Passed: [0;32m17[0m
|
||||
Failed: [0;31m0[0m
|
||||
[0;32m✓ All memory corruption tests passed![0m
|
||||
The relay appears to handle memory safely.
|
||||
2026-04-01 09:57:40 - \033[0;32m✓ Memory Corruption Tests PASSED\033[0m (Duration: 1s)
|
||||
2026-04-01 09:57:40 - ==========================================
|
||||
2026-04-01 09:57:40 - Running Test Suite: Input Validation Tests
|
||||
2026-04-01 09:57:40 - Description: Comprehensive input boundary testing
|
||||
2026-04-01 09:57:40 - ==========================================
|
||||
==========================================
|
||||
C-Relay Input Validation Test Suite
|
||||
==========================================
|
||||
Testing against relay at ws://127.0.0.1:8888
|
||||
|
||||
=== Basic Connectivity Test ===
|
||||
Testing Basic connectivity... [0;32mPASSED[0m - Input accepted correctly
|
||||
|
||||
=== Message Type Validation ===
|
||||
Testing Invalid message type - string... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Invalid message type - number... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Invalid message type - null... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Invalid message type - object... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Empty message type... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Very long message type... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== Message Structure Validation ===
|
||||
Testing Too few arguments... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Too many arguments... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Non-array message... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Empty array... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Nested arrays incorrectly... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== Subscription ID Boundary Tests ===
|
||||
Testing Valid subscription ID... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Empty subscription ID... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Subscription ID with spaces... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Subscription ID with newlines... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Subscription ID with tabs... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Subscription ID with control chars... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Unicode subscription ID... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Very long subscription ID... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== Filter Object Validation ===
|
||||
Testing Valid empty filter... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Non-object filter... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Null filter... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Array filter... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Filter with invalid keys... [0;32mPASSED[0m - Input accepted correctly
|
||||
|
||||
=== Authors Field Validation ===
|
||||
Testing Valid authors array... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Empty authors array... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Non-array authors... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Invalid hex in authors... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Short pubkey in authors... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== IDs Field Validation ===
|
||||
Testing Valid ids array... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Empty ids array... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Non-array ids... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== Kinds Field Validation ===
|
||||
Testing Valid kinds array... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Empty kinds array... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Non-array kinds... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing String in kinds... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== Timestamp Field Validation ===
|
||||
Testing Valid since timestamp... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Valid until timestamp... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing String since timestamp... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Negative timestamp... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== Limit Field Validation ===
|
||||
Testing Valid limit... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Zero limit... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing String limit... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
Testing Negative limit... [0;32mPASSED[0m - Invalid input properly rejected
|
||||
|
||||
=== Multiple Filters ===
|
||||
Testing Two valid filters... [0;32mPASSED[0m - Input accepted correctly
|
||||
Testing Many filters... [0;32mPASSED[0m - Input accepted correctly
|
||||
|
||||
=== Test Results ===
|
||||
Total tests: 47
|
||||
Passed: 47
|
||||
Failed: 0
|
||||
[0;32m✓ All input validation tests passed![0m
|
||||
The relay properly validates input.
|
||||
2026-04-01 09:57:41 - \033[0;32m✓ Input Validation Tests PASSED\033[0m (Duration: 1s)
|
||||
2026-04-01 09:57:41 -
|
||||
2026-04-01 09:57:41 - \033[0;34m=== PERFORMANCE TEST SUITES ===\033[0m
|
||||
2026-04-01 09:57:41 - ==========================================
|
||||
2026-04-01 09:57:41 - Running Test Suite: Subscription Limit Tests
|
||||
2026-04-01 09:57:41 - Description: Subscription limit enforcement testing
|
||||
2026-04-01 09:57:41 - ==========================================
|
||||
=== Subscription Limit Test ===
|
||||
[INFO] Testing relay at: ws://127.0.0.1:8888
|
||||
[INFO] Note: This test assumes default subscription limits (max 25 per client)
|
||||
|
||||
=== Test 1: Basic Connectivity ===
|
||||
[INFO] Testing basic WebSocket connection...
|
||||
[PASS] Basic connectivity works
|
||||
|
||||
=== Test 2: Subscription Limit Enforcement ===
|
||||
[INFO] Testing subscription limits by creating multiple subscriptions...
|
||||
[INFO] Creating multiple subscriptions within a single connection...
|
||||
[INFO] Hit subscription limit at subscription 2
|
||||
[PASS] Subscription limit enforcement working (limit hit after 1 subscriptions)
|
||||
|
||||
=== Test Complete ===
|
||||
2026-04-01 09:57:42 - \033[0;32m✓ Subscription Limit Tests PASSED\033[0m (Duration: 1s)
|
||||
2026-04-01 09:57:42 - ==========================================
|
||||
2026-04-01 09:57:42 - Running Test Suite: Load Testing
|
||||
2026-04-01 09:57:42 - Description: High concurrent connection testing
|
||||
2026-04-01 09:57:42 - ==========================================
|
||||
==========================================
|
||||
C-Relay Load Testing Suite
|
||||
==========================================
|
||||
Testing against relay at ws://127.0.0.1:8888
|
||||
|
||||
=== Basic Connectivity Test ===
|
||||
[0;32m✓ Relay is accessible[0m
|
||||
|
||||
==========================================
|
||||
Load Test: Light Load Test
|
||||
Description: Basic load test with moderate concurrent connections
|
||||
Concurrent clients: 10
|
||||
Messages per client: 5
|
||||
==========================================
|
||||
Launching 10 clients...
|
||||
All clients completed. Processing results...
|
||||
|
||||
=== Load Test Results ===
|
||||
Test duration: 1s
|
||||
Total connections attempted: 10
|
||||
Successful connections: 10
|
||||
Failed connections: 0
|
||||
Connection success rate: 100%
|
||||
Messages expected: 50
|
||||
Messages sent: 50
|
||||
Messages received: 100
|
||||
[0;32m✓ EXCELLENT: High connection success rate[0m
|
||||
|
||||
Checking relay responsiveness... [0;32m✓ Relay is still responsive[0m
|
||||
|
||||
==========================================
|
||||
Load Test: Medium Load Test
|
||||
Description: Moderate load test with higher concurrency
|
||||
Concurrent clients: 25
|
||||
Messages per client: 10
|
||||
==========================================
|
||||
Launching 25 clients...
|
||||
All clients completed. Processing results...
|
||||
|
||||
=== Load Test Results ===
|
||||
Test duration: 4s
|
||||
Total connections attempted: 35
|
||||
Successful connections: 25
|
||||
Failed connections: 0
|
||||
Connection success rate: 71%
|
||||
Messages expected: 250
|
||||
Messages sent: 250
|
||||
Messages received: 500
|
||||
[0;31m✗ POOR: Low connection success rate[0m
|
||||
|
||||
Checking relay responsiveness... [0;32m✓ Relay is still responsive[0m
|
||||
|
||||
==========================================
|
||||
Load Test: Heavy Load Test
|
||||
Description: Heavy load test with high concurrency
|
||||
Concurrent clients: 50
|
||||
Messages per client: 20
|
||||
==========================================
|
||||
Launching 50 clients...
|
||||
All clients completed. Processing results...
|
||||
|
||||
=== Load Test Results ===
|
||||
Test duration: 15s
|
||||
Total connections attempted: 85
|
||||
Successful connections: 50
|
||||
Failed connections: 0
|
||||
Connection success rate: 58%
|
||||
Messages expected: 1000
|
||||
Messages sent: 1000
|
||||
Messages received: 2000
|
||||
[0;31m✗ POOR: Low connection success rate[0m
|
||||
|
||||
Checking relay responsiveness... [0;32m✓ Relay is still responsive[0m
|
||||
|
||||
==========================================
|
||||
Load Test: Stress Test
|
||||
Description: Maximum load test to find breaking point
|
||||
Concurrent clients: 100
|
||||
Messages per client: 50
|
||||
==========================================
|
||||
Launching 100 clients...
|
||||
All clients completed. Processing results...
|
||||
|
||||
=== Load Test Results ===
|
||||
Test duration: 64s
|
||||
Total connections attempted: 185
|
||||
Successful connections: 100
|
||||
Failed connections: 0
|
||||
Connection success rate: 54%
|
||||
Messages expected: 5000
|
||||
Messages sent: 5000
|
||||
Messages received: 7500
|
||||
[0;31m✗ POOR: Low connection success rate[0m
|
||||
|
||||
Checking relay responsiveness... [0;32m✓ Relay is still responsive[0m
|
||||
|
||||
==========================================
|
||||
Load Testing Complete
|
||||
==========================================
|
||||
All load tests completed. Check individual test results above.
|
||||
If any tests failed, the relay may need optimization or have resource limits.
|
||||
2026-04-01 09:59:07 - \033[0;32m✓ Load Testing PASSED\033[0m (Duration: 85s)
|
||||
2026-04-01 09:59:07 - ==========================================
|
||||
2026-04-01 09:59:07 - Running Test Suite: Stress Testing
|
||||
2026-04-01 09:59:07 - Description: Resource usage and stability testing
|
||||
2026-04-01 09:59:07 - ==========================================
|
||||
2026-04-01 09:59:07 - \033[0;31mERROR: Test script stress_tests.sh not found\033[0m
|
||||
Reference in New Issue
Block a user