Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be99595bde |
@@ -1250,6 +1250,7 @@
|
|||||||
console.log('Exited edit mode');
|
console.log('Exited edit mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function saveConfiguration() {
|
async function saveConfiguration() {
|
||||||
if (!isLoggedIn || !userPubkey) {
|
if (!isLoggedIn || !userPubkey) {
|
||||||
console.log('Must be logged in to save configuration');
|
console.log('Must be logged in to save configuration');
|
||||||
@@ -1291,9 +1292,9 @@
|
|||||||
content: currentConfig.content || 'C Nostr Relay Configuration'
|
content: currentConfig.content || 'C Nostr Relay Configuration'
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Signing event with window.nostr...');
|
console.log('Signing event with window.nostr.signEvent()...');
|
||||||
|
|
||||||
// Sign the event using window.nostr (NIP-07 interface)
|
// Sign the event using the standard NIP-07 interface
|
||||||
const signedEvent = await window.nostr.signEvent(newEvent);
|
const signedEvent = await window.nostr.signEvent(newEvent);
|
||||||
|
|
||||||
if (!signedEvent || !signedEvent.sig) {
|
if (!signedEvent || !signedEvent.sig) {
|
||||||
@@ -1941,13 +1942,8 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show warning about whitelist-only mode
|
statusDiv.className = 'rule-status';
|
||||||
if (warningDiv) {
|
statusDiv.textContent = 'Adding to whitelist...';
|
||||||
warningDiv.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
statusDiv.className = 'rule-status warning';
|
|
||||||
statusDiv.textContent = 'Adding to whitelist (will enable whitelist-only mode)...';
|
|
||||||
|
|
||||||
// Create auth rule data
|
// Create auth rule data
|
||||||
const ruleData = {
|
const ruleData = {
|
||||||
@@ -2038,33 +2034,64 @@
|
|||||||
try {
|
try {
|
||||||
log(`Adding auth rule: ${ruleData.rule_type} - ${ruleData.pattern_value.substring(0, 16)}...`, 'INFO');
|
log(`Adding auth rule: ${ruleData.rule_type} - ${ruleData.pattern_value.substring(0, 16)}...`, 'INFO');
|
||||||
|
|
||||||
// Create kind 33335 auth rule event
|
// Map client-side rule types to database schema values
|
||||||
|
let dbRuleType, dbPatternType, dbAction;
|
||||||
|
|
||||||
|
switch (ruleData.rule_type) {
|
||||||
|
case 'pubkey_blacklist':
|
||||||
|
dbRuleType = 'blacklist';
|
||||||
|
dbPatternType = 'pubkey';
|
||||||
|
dbAction = 'deny';
|
||||||
|
break;
|
||||||
|
case 'pubkey_whitelist':
|
||||||
|
dbRuleType = 'whitelist';
|
||||||
|
dbPatternType = 'pubkey';
|
||||||
|
dbAction = 'allow';
|
||||||
|
break;
|
||||||
|
case 'hash_blacklist':
|
||||||
|
dbRuleType = 'blacklist';
|
||||||
|
dbPatternType = 'pubkey'; // Schema supports: pubkey, kind, ip, global - using pubkey for hash for now
|
||||||
|
dbAction = 'deny';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown rule type: ${ruleData.rule_type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map pattern type to database schema values
|
||||||
|
if (ruleData.pattern_type === 'Global') {
|
||||||
|
dbPatternType = 'global';
|
||||||
|
} else if (ruleData.pattern_type === 'pubkey') {
|
||||||
|
dbPatternType = 'pubkey';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create kind 33335 auth rule event with database schema values
|
||||||
const authEvent = {
|
const authEvent = {
|
||||||
kind: 33335,
|
kind: 33335,
|
||||||
pubkey: userPubkey,
|
pubkey: userPubkey,
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
tags: [
|
tags: [
|
||||||
['d', 'auth-rules'], // Addressable event identifier
|
['d', 'auth-rules'], // Addressable event identifier
|
||||||
[ruleData.rule_type, ruleData.pattern_type, ruleData.pattern_value]
|
[dbRuleType, dbPatternType, ruleData.pattern_value]
|
||||||
],
|
],
|
||||||
content: JSON.stringify({
|
content: JSON.stringify({
|
||||||
action: 'add',
|
action: 'add',
|
||||||
rule_type: ruleData.rule_type,
|
rule_type: dbRuleType,
|
||||||
pattern_type: ruleData.pattern_type,
|
pattern_type: dbPatternType,
|
||||||
pattern_value: ruleData.pattern_value,
|
pattern_value: ruleData.pattern_value,
|
||||||
rule_action: ruleData.action
|
rule_action: dbAction
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// DEBUG: Log the complete event structure being sent
|
// DEBUG: Log the complete event structure being sent
|
||||||
console.log('=== AUTH RULE EVENT DEBUG ===');
|
console.log('=== AUTH RULE EVENT DEBUG ===');
|
||||||
console.log('Rule Data:', ruleData);
|
console.log('Original Rule Data:', ruleData);
|
||||||
|
console.log('Mapped DB Values:', { dbRuleType, dbPatternType, dbAction });
|
||||||
console.log('Auth Event (before signing):', JSON.stringify(authEvent, null, 2));
|
console.log('Auth Event (before signing):', JSON.stringify(authEvent, null, 2));
|
||||||
console.log('Auth Event Tags:', authEvent.tags);
|
console.log('Auth Event Tags:', authEvent.tags);
|
||||||
console.log('Auth Event Content:', authEvent.content);
|
console.log('Auth Event Content:', authEvent.content);
|
||||||
console.log('=== END AUTH RULE EVENT DEBUG ===');
|
console.log('=== END AUTH RULE EVENT DEBUG ===');
|
||||||
|
|
||||||
// Sign the event
|
// Sign the event using the standard NIP-07 interface
|
||||||
const signedEvent = await window.nostr.signEvent(authEvent);
|
const signedEvent = await window.nostr.signEvent(authEvent);
|
||||||
if (!signedEvent || !signedEvent.sig) {
|
if (!signedEvent || !signedEvent.sig) {
|
||||||
throw new Error('Event signing failed');
|
throw new Error('Event signing failed');
|
||||||
|
|||||||
20
src/config.c
20
src/config.c
@@ -2181,11 +2181,16 @@ int process_admin_auth_event(cJSON* event, char* error_message, size_t error_siz
|
|||||||
|
|
||||||
// Parse the action from content (should be "add" or "remove")
|
// Parse the action from content (should be "add" or "remove")
|
||||||
cJSON* content_json = cJSON_Parse(content);
|
cJSON* content_json = cJSON_Parse(content);
|
||||||
const char* action = "add"; // default
|
char action_buffer[16] = "add"; // Local buffer for action string
|
||||||
|
const char* action = action_buffer; // default
|
||||||
if (content_json) {
|
if (content_json) {
|
||||||
cJSON* action_obj = cJSON_GetObjectItem(content_json, "action");
|
cJSON* action_obj = cJSON_GetObjectItem(content_json, "action");
|
||||||
if (action_obj && cJSON_IsString(action_obj)) {
|
if (action_obj && cJSON_IsString(action_obj)) {
|
||||||
action = cJSON_GetStringValue(action_obj);
|
const char* action_str = cJSON_GetStringValue(action_obj);
|
||||||
|
if (action_str) {
|
||||||
|
strncpy(action_buffer, action_str, sizeof(action_buffer) - 1);
|
||||||
|
action_buffer[sizeof(action_buffer) - 1] = '\0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cJSON_Delete(content_json);
|
cJSON_Delete(content_json);
|
||||||
}
|
}
|
||||||
@@ -2248,19 +2253,10 @@ int process_admin_auth_event(cJSON* event, char* error_message, size_t error_siz
|
|||||||
printf(" Extracted rule: type='%s', pattern_type='%s', pattern_value='%s'\n",
|
printf(" Extracted rule: type='%s', pattern_type='%s', pattern_value='%s'\n",
|
||||||
rule_type, pattern_type, pattern_value);
|
rule_type, pattern_type, pattern_value);
|
||||||
|
|
||||||
// Map rule_type to correct action (FIX THE BUG HERE)
|
|
||||||
const char* mapped_action = "allow"; // default
|
|
||||||
if (strcmp(rule_type, "pubkey_blacklist") == 0 || strcmp(rule_type, "hash_blacklist") == 0) {
|
|
||||||
mapped_action = "deny";
|
|
||||||
} else if (strcmp(rule_type, "pubkey_whitelist") == 0) {
|
|
||||||
mapped_action = "allow";
|
|
||||||
}
|
|
||||||
printf(" Mapped action for rule_type '%s': '%s'\n", rule_type, mapped_action);
|
|
||||||
|
|
||||||
// Process the auth rule based on action
|
// Process the auth rule based on action
|
||||||
if (strcmp(action, "add") == 0) {
|
if (strcmp(action, "add") == 0) {
|
||||||
printf(" Attempting to add rule to database...\n");
|
printf(" Attempting to add rule to database...\n");
|
||||||
if (add_auth_rule_from_config(rule_type, pattern_type, pattern_value, mapped_action) == 0) {
|
if (add_auth_rule_from_config(rule_type, pattern_type, pattern_value, "allow") == 0) {
|
||||||
printf(" SUCCESS: Rule added to database\n");
|
printf(" SUCCESS: Rule added to database\n");
|
||||||
rules_processed++;
|
rules_processed++;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
83
src/main.c
83
src/main.c
@@ -3115,11 +3115,30 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
cJSON* kind_obj = cJSON_GetObjectItem(event_obj, "kind");
|
cJSON* kind_obj = cJSON_GetObjectItem(event_obj, "kind");
|
||||||
int event_kind = kind_obj && cJSON_IsNumber(kind_obj) ? (int)cJSON_GetNumberValue(kind_obj) : -1;
|
int event_kind = kind_obj && cJSON_IsNumber(kind_obj) ? (int)cJSON_GetNumberValue(kind_obj) : -1;
|
||||||
|
|
||||||
|
// Extract pubkey and event ID for debugging
|
||||||
|
cJSON* pubkey_obj = cJSON_GetObjectItem(event_obj, "pubkey");
|
||||||
|
cJSON* id_obj = cJSON_GetObjectItem(event_obj, "id");
|
||||||
|
const char* event_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : "unknown";
|
||||||
|
const char* event_id = id_obj ? cJSON_GetStringValue(id_obj) : "unknown";
|
||||||
|
|
||||||
|
char debug_event_msg[512];
|
||||||
|
snprintf(debug_event_msg, sizeof(debug_event_msg),
|
||||||
|
"DEBUG EVENT: Processing kind %d event from pubkey %.16s... ID %.16s...",
|
||||||
|
event_kind, event_pubkey, event_id);
|
||||||
|
log_info(debug_event_msg);
|
||||||
|
|
||||||
// Check if NIP-42 authentication is required for this event kind or globally
|
// Check if NIP-42 authentication is required for this event kind or globally
|
||||||
int auth_required = is_nip42_auth_globally_required() || is_nip42_auth_required_for_kind(event_kind);
|
int auth_required = is_nip42_auth_globally_required() || is_nip42_auth_required_for_kind(event_kind);
|
||||||
|
|
||||||
|
char debug_auth_msg[256];
|
||||||
|
snprintf(debug_auth_msg, sizeof(debug_auth_msg),
|
||||||
|
"DEBUG AUTH: auth_required=%d, pss->authenticated=%d, event_kind=%d",
|
||||||
|
auth_required, pss ? pss->authenticated : -1, event_kind);
|
||||||
|
log_info(debug_auth_msg);
|
||||||
|
|
||||||
if (pss && auth_required && !pss->authenticated) {
|
if (pss && auth_required && !pss->authenticated) {
|
||||||
if (!pss->auth_challenge_sent) {
|
if (!pss->auth_challenge_sent) {
|
||||||
|
log_info("DEBUG AUTH: Sending NIP-42 authentication challenge");
|
||||||
send_nip42_auth_challenge(wsi, pss);
|
send_nip42_auth_challenge(wsi, pss);
|
||||||
} else {
|
} else {
|
||||||
char auth_msg[256];
|
char auth_msg[256];
|
||||||
@@ -3170,6 +3189,8 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_info("DEBUG VALIDATION: Starting unified validator");
|
||||||
|
|
||||||
// Call unified validator with JSON string
|
// Call unified validator with JSON string
|
||||||
size_t event_json_len = strlen(event_json_str);
|
size_t event_json_len = strlen(event_json_str);
|
||||||
int validation_result = nostr_validate_unified_request(event_json_str, event_json_len);
|
int validation_result = nostr_validate_unified_request(event_json_str, event_json_len);
|
||||||
@@ -3177,6 +3198,11 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
// Map validation result to old result format (0 = success, -1 = failure)
|
// Map validation result to old result format (0 = success, -1 = failure)
|
||||||
int result = (validation_result == NOSTR_SUCCESS) ? 0 : -1;
|
int result = (validation_result == NOSTR_SUCCESS) ? 0 : -1;
|
||||||
|
|
||||||
|
char debug_validation_msg[256];
|
||||||
|
snprintf(debug_validation_msg, sizeof(debug_validation_msg),
|
||||||
|
"DEBUG VALIDATION: validation_result=%d, result=%d", validation_result, result);
|
||||||
|
log_info(debug_validation_msg);
|
||||||
|
|
||||||
// Generate error message based on validation result
|
// Generate error message based on validation result
|
||||||
char error_message[512] = {0};
|
char error_message[512] = {0};
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
@@ -3206,8 +3232,12 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
strncpy(error_message, "error: validation failed", sizeof(error_message) - 1);
|
strncpy(error_message, "error: validation failed", sizeof(error_message) - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
char debug_error_msg[256];
|
||||||
|
snprintf(debug_error_msg, sizeof(debug_error_msg),
|
||||||
|
"DEBUG VALIDATION ERROR: %s", error_message);
|
||||||
|
log_warning(debug_error_msg);
|
||||||
} else {
|
} else {
|
||||||
log_info("Event validated successfully using unified validator");
|
log_info("DEBUG VALIDATION: Event validated successfully using unified validator");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup event JSON string
|
// Cleanup event JSON string
|
||||||
@@ -3219,42 +3249,62 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
if (kind_obj && cJSON_IsNumber(kind_obj)) {
|
if (kind_obj && cJSON_IsNumber(kind_obj)) {
|
||||||
int event_kind = (int)cJSON_GetNumberValue(kind_obj);
|
int event_kind = (int)cJSON_GetNumberValue(kind_obj);
|
||||||
|
|
||||||
|
log_info("DEBUG ADMIN: Checking if admin event processing is needed");
|
||||||
|
|
||||||
if (event_kind == 33334 || event_kind == 33335) {
|
if (event_kind == 33334 || event_kind == 33335) {
|
||||||
// This is an admin event - process it through the admin API instead of normal storage
|
// This is an admin event - process it through the admin API instead of normal storage
|
||||||
log_info("Admin event detected, processing through admin API");
|
log_info("DEBUG ADMIN: Admin event detected, processing through admin API");
|
||||||
|
|
||||||
char admin_error[512] = {0};
|
char admin_error[512] = {0};
|
||||||
if (process_admin_event_in_config(event, admin_error, sizeof(admin_error)) != 0) {
|
int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error));
|
||||||
log_error("Failed to process admin event through admin API");
|
|
||||||
|
char debug_admin_msg[256];
|
||||||
|
snprintf(debug_admin_msg, sizeof(debug_admin_msg),
|
||||||
|
"DEBUG ADMIN: process_admin_event_in_config returned %d", admin_result);
|
||||||
|
log_info(debug_admin_msg);
|
||||||
|
|
||||||
|
if (admin_result != 0) {
|
||||||
|
log_error("DEBUG ADMIN: Failed to process admin event through admin API");
|
||||||
result = -1;
|
result = -1;
|
||||||
size_t error_len = strlen(admin_error);
|
size_t error_len = strlen(admin_error);
|
||||||
size_t copy_len = (error_len < sizeof(error_message) - 1) ? error_len : sizeof(error_message) - 1;
|
size_t copy_len = (error_len < sizeof(error_message) - 1) ? error_len : sizeof(error_message) - 1;
|
||||||
memcpy(error_message, admin_error, copy_len);
|
memcpy(error_message, admin_error, copy_len);
|
||||||
error_message[copy_len] = '\0';
|
error_message[copy_len] = '\0';
|
||||||
|
|
||||||
|
char debug_admin_error_msg[600];
|
||||||
|
snprintf(debug_admin_error_msg, sizeof(debug_admin_error_msg),
|
||||||
|
"DEBUG ADMIN ERROR: %.400s", admin_error);
|
||||||
|
log_error(debug_admin_error_msg);
|
||||||
} else {
|
} else {
|
||||||
log_success("Admin event processed successfully through admin API");
|
log_success("DEBUG ADMIN: Admin event processed successfully through admin API");
|
||||||
// Admin events are processed by the admin API, not broadcast to subscriptions
|
// Admin events are processed by the admin API, not broadcast to subscriptions
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Regular event - store in database and broadcast
|
// Regular event - store in database and broadcast
|
||||||
|
log_info("DEBUG STORAGE: Regular event - storing in database");
|
||||||
if (store_event(event) != 0) {
|
if (store_event(event) != 0) {
|
||||||
log_error("Failed to store event in database");
|
log_error("DEBUG STORAGE: Failed to store event in database");
|
||||||
result = -1;
|
result = -1;
|
||||||
strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1);
|
strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1);
|
||||||
} else {
|
} else {
|
||||||
log_info("Event stored successfully in database");
|
log_info("DEBUG STORAGE: Event stored successfully in database");
|
||||||
// Broadcast event to matching persistent subscriptions
|
// Broadcast event to matching persistent subscriptions
|
||||||
broadcast_event_to_subscriptions(event);
|
int broadcast_count = broadcast_event_to_subscriptions(event);
|
||||||
|
char debug_broadcast_msg[128];
|
||||||
|
snprintf(debug_broadcast_msg, sizeof(debug_broadcast_msg),
|
||||||
|
"DEBUG BROADCAST: Event broadcast to %d subscriptions", broadcast_count);
|
||||||
|
log_info(debug_broadcast_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Event without valid kind - try normal storage
|
// Event without valid kind - try normal storage
|
||||||
|
log_warning("DEBUG STORAGE: Event without valid kind - trying normal storage");
|
||||||
if (store_event(event) != 0) {
|
if (store_event(event) != 0) {
|
||||||
log_error("Failed to store event in database");
|
log_error("DEBUG STORAGE: Failed to store event without kind in database");
|
||||||
result = -1;
|
result = -1;
|
||||||
strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1);
|
strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1);
|
||||||
} else {
|
} else {
|
||||||
log_info("Event stored successfully in database");
|
log_info("DEBUG STORAGE: Event without kind stored successfully in database");
|
||||||
broadcast_event_to_subscriptions(event);
|
broadcast_event_to_subscriptions(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3272,11 +3322,22 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso
|
|||||||
// TODO: REPLACE - Remove wasteful cJSON_Print conversion
|
// TODO: REPLACE - Remove wasteful cJSON_Print conversion
|
||||||
char *response_str = cJSON_Print(response);
|
char *response_str = cJSON_Print(response);
|
||||||
if (response_str) {
|
if (response_str) {
|
||||||
|
char debug_response_msg[512];
|
||||||
|
snprintf(debug_response_msg, sizeof(debug_response_msg),
|
||||||
|
"DEBUG RESPONSE: Sending OK response: %s", response_str);
|
||||||
|
log_info(debug_response_msg);
|
||||||
|
|
||||||
size_t response_len = strlen(response_str);
|
size_t response_len = strlen(response_str);
|
||||||
unsigned char *buf = malloc(LWS_PRE + response_len);
|
unsigned char *buf = malloc(LWS_PRE + response_len);
|
||||||
if (buf) {
|
if (buf) {
|
||||||
memcpy(buf + LWS_PRE, response_str, response_len);
|
memcpy(buf + LWS_PRE, response_str, response_len);
|
||||||
lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT);
|
int write_result = lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT);
|
||||||
|
|
||||||
|
char debug_write_msg[128];
|
||||||
|
snprintf(debug_write_msg, sizeof(debug_write_msg),
|
||||||
|
"DEBUG RESPONSE: lws_write returned %d", write_result);
|
||||||
|
log_info(debug_write_msg);
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
free(response_str);
|
free(response_str);
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
=== NIP-42 Authentication Test Started ===
|
|
||||||
2025-09-13 08:48:02 - Starting NIP-42 authentication tests
|
|
||||||
[34m[1m[INFO][0m === Starting NIP-42 Authentication Tests ===
|
|
||||||
[34m[1m[INFO][0m Checking dependencies...
|
|
||||||
[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
|
|
||||||
2025-09-13 08:48:02 - Supported NIPs: 1,9,11,13,15,20,40,42
|
|
||||||
[34m[1m[INFO][0m Test 2: Testing AUTH challenge generation
|
|
||||||
[34m[1m[INFO][0m Found admin private key, configuring NIP-42 authentication...
|
|
||||||
[33m[1m[WARNING][0m Failed to create configuration event - proceeding with manual test
|
|
||||||
[34m[1m[INFO][0m Test 3: Testing complete NIP-42 authentication flow
|
|
||||||
[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...
|
|
||||||
2025-09-13 08:48:03 - Event publish result: connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":1,"id":"c42a8cbdd1cc6ea3e7fd060919c57386aef0c35da272ba2fa34b45f80934cfca","pubkey":"d0111448b3bd0da6aa699b92163f684291bb43bc213aa54a2ee726c2acde76e8","created_at":1757767683,"tags":[],"content":"NIP-42 test event - should require auth","sig":"d2a2c7efc00e06d8d8582fa05b2ec8cb96979525770dff9ef36a91df6d53807c86115581de2d6058d7d64eebe3b7d7404cc03dbb2ad1e91d140283703c2dec53"}
|
|
||||||
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
|
|
||||||
[34m[1m[INFO][0m Testing WebSocket connection and AUTH message...
|
|
||||||
[34m[1m[INFO][0m Sending test message via WebSocket...
|
|
||||||
2025-09-13 08:48:03 - WebSocket response:
|
|
||||||
[34m[1m[INFO][0m No AUTH challenge in WebSocket response
|
|
||||||
[34m[1m[INFO][0m Test 5: Testing NIP-42 configuration options
|
|
||||||
[34m[1m[INFO][0m Retrieving current relay configuration...
|
|
||||||
[32m[1m[SUCCESS][0m Retrieved configuration events from relay
|
|
||||||
[32m[1m[SUCCESS][0m Found NIP-42 configuration:
|
|
||||||
2025-09-13 08:48:04 - nip42_auth_required_events=false
|
|
||||||
2025-09-13 08:48:04 - nip42_auth_required_subscriptions=false
|
|
||||||
2025-09-13 08:48:04 - nip42_auth_required_kinds=4,14
|
|
||||||
2025-09-13 08:48:04 - nip42_challenge_expiration=600
|
|
||||||
[34m[1m[INFO][0m Test 6: Testing NIP-42 performance and stability
|
|
||||||
[34m[1m[INFO][0m Testing multiple authentication attempts...
|
|
||||||
2025-09-13 08:48:05 - Attempt 1: .271641300s - connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":1,"id":"916049dbd6835443e8fd553bd12a37ef03060a01fedb099b414ea2cc18b597eb","pubkey":"b383f405d81860ec9b0eebf88612093ab18dc6abd322639b19ac79969599c8c4","created_at":1757767685,"tags":[],"content":"Performance test event 1","sig":"b04e0b38bbb49e0aa3c8a69530071bb08d917c4ba12eae38045a487c43e83f6dc1389ac4640453b0492d9c991df37f71e25ef501fd48c4c11c878e6cb3fa7a84"}
|
|
||||||
publishing to ws://localhost:8888... success.
|
|
||||||
2025-09-13 08:48:05 - Attempt 2: .259343520s - connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":1,"id":"e4495a56ec6f1ba2759eabbf0128aec615c53acf3e4720be7726dcd7163da703","pubkey":"b383f405d81860ec9b0eebf88612093ab18dc6abd322639b19ac79969599c8c4","created_at":1757767685,"tags":[],"content":"Performance test event 2","sig":"d1efe3f576eeded4e292ec22f2fea12296fa17ed2f87a8cd2dde0444b594ef55f7d74b680aeca11295a16397df5ccc53a938533947aece27efb965e6c643b62c"}
|
|
||||||
publishing to ws://localhost:8888... success.
|
|
||||||
2025-09-13 08:48:06 - Attempt 3: .221167032s - connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":1,"id":"55035b4c95a2c93a169236c7f5f5bd627838ec13522c88cf82d8b55516560cd9","pubkey":"b383f405d81860ec9b0eebf88612093ab18dc6abd322639b19ac79969599c8c4","created_at":1757767686,"tags":[],"content":"Performance test event 3","sig":"4bd581580a5a2416e6a9af44c055333635832dbf21793517f16100f1366c73437659545a8a712dcc4623a801b9deccd372b36b658309e7102a4300c3f481facb"}
|
|
||||||
publishing to ws://localhost:8888... success.
|
|
||||||
2025-09-13 08:48:06 - Attempt 4: .260219496s - connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":1,"id":"58dee587a1a0f085ff44441b3074f5ff42715088ee24e694107100df3c63ff2b","pubkey":"b383f405d81860ec9b0eebf88612093ab18dc6abd322639b19ac79969599c8c4","created_at":1757767686,"tags":[],"content":"Performance test event 4","sig":"b6174b0c56138466d3bb228ef2ced1d917f7253b76c624235fa3b661c9fa109c78ae557c4ddaf0e6232aa597608916f0dfba1c192f8b90ffb819c36ac1e4e516"}
|
|
||||||
publishing to ws://localhost:8888... success.
|
|
||||||
2025-09-13 08:48:07 - Attempt 5: .260125188s - connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":1,"id":"b8069c80f98fff3780eaeb605baf1a5818c9ab05185c1776a28469d2b0b32c6a","pubkey":"b383f405d81860ec9b0eebf88612093ab18dc6abd322639b19ac79969599c8c4","created_at":1757767687,"tags":[],"content":"Performance test event 5","sig":"5130d3a0c778728747b12aae77f2516db5b055d8ec43f413a4b117fcadb6025a49b6f602307bbe758bd97557e326e8735631fd03dc45c9296509e94aa305adf2"}
|
|
||||||
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...
|
|
||||||
2025-09-13 08:48:08 - Kind 1 event result: connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":1,"id":"f2ac02a5290db3797c0b7b38435920d5db593d333e582454d8ed32da4c141b74","pubkey":"da031504ff61656d1829f723c52f526d7591400fb9e2aecb7b4ef5aeeea66fc7","created_at":1757767688,"tags":[],"content":"Regular note - should not require auth","sig":"8e4272d9cb258fc4b140eb8e8c2e802c3e8b62e34c17c9e545d83c68dfb86ffd2cdd4a8153660b663a46906459aa67719257ac263f21d1f8a6185806e055dcfd"}
|
|
||||||
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...
|
|
||||||
2025-09-13 08:48:18 - Kind 4 event result: connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":4,"id":"935af23e2bf7efd324d86a0c82631e5ebe492edf21920ed0f548faa73a18ac1d","pubkey":"da031504ff61656d1829f723c52f526d7591400fb9e2aecb7b4ef5aeeea66fc7","created_at":1757767688,"tags":[["p,test_pubkey"]],"content":"This is a direct message - should require auth","sig":"b2b86ee394b41505ddbd787c22f4223665770d84a21dd03e74bf4e8fa879ff82dd6b1f7d6921d93f8d89787102c3dc3012e6270d66ca5b5d4b87f1a545481e76"}
|
|
||||||
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...
|
|
||||||
2025-09-13 08:48:28 - Kind 14 event result: connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":14,"id":"aeb1ac58dd465c90ce5a70c7b16e3cc32fae86c221bb2e86ca29934333604669","pubkey":"da031504ff61656d1829f723c52f526d7591400fb9e2aecb7b4ef5aeeea66fc7","created_at":1757767698,"tags":[["p,test_pubkey"]],"content":"Chat message - should require auth","sig":"24e23737e6684e4ef01c08d72304e6f235ce75875b94b37460065f9ead986438435585818ba104e7f78f14345406b5d03605c925042e9c06fed8c99369cd8694"}
|
|
||||||
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...
|
|
||||||
2025-09-13 08:48:29 - Kind 0 event result: connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":0,"id":"3b2cc834dd874ebbe07c2da9e41c07b3f0c61a57b4d6b7299c2243dbad29f2ca","pubkey":"da031504ff61656d1829f723c52f526d7591400fb9e2aecb7b4ef5aeeea66fc7","created_at":1757767709,"tags":[],"content":"Test event kind 0 - should not require auth","sig":"4f2016fde84d72cf5a5aa4c0ec5de677ef06c7971ca2dd756b02a94c47604fae1c67254703a2df3d17b13fee2d9c45661b76086f29ac93820a4c062fc52dea74"}
|
|
||||||
publishing to ws://localhost:8888... success.
|
|
||||||
[32m[1m[SUCCESS][0m Kind 0 event accepted without authentication (correct)
|
|
||||||
2025-09-13 08:48:29 - Kind 3 event result: connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":3,"id":"6e1ea0b1cbf342feea030fa39226c316e730c5d333fa8333495748afd386ec80","pubkey":"da031504ff61656d1829f723c52f526d7591400fb9e2aecb7b4ef5aeeea66fc7","created_at":1757767709,"tags":[],"content":"Test event kind 3 - should not require auth","sig":"e5f66c5f022497f8888f003a8bfbb5e807a2520d314c80889548efa267f9d6de28d5ee7b0588cc8660f2963ab44e530c8a74d71a227148e5a6843fcef4de2197"}
|
|
||||||
publishing to ws://localhost:8888... success.
|
|
||||||
[32m[1m[SUCCESS][0m Kind 3 event accepted without authentication (correct)
|
|
||||||
2025-09-13 08:48:30 - Kind 7 event result: connecting to ws://localhost:8888... ok.
|
|
||||||
{"kind":7,"id":"a64466b9899cad257313e2dced357fd3f87f40bd7e13e29372689aae7c718919","pubkey":"da031504ff61656d1829f723c52f526d7591400fb9e2aecb7b4ef5aeeea66fc7","created_at":1757767710,"tags":[],"content":"Test event kind 7 - should not require auth","sig":"78d18bcb0c2b11b4e2b74bcdfb140564b4563945e983014a279977356e50b57f3c5a262fa55de26dbd4c8d8b9f5beafbe21af869be64079f54a712284f03d9ac"}
|
|
||||||
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
|
|
||||||
[34m[1m[INFO][0m === NIP-42 Test Results Summary ===
|
|
||||||
[32m[1m[SUCCESS][0m Dependencies: PASS
|
|
||||||
[32m[1m[SUCCESS][0m NIP-42 Support: PASS
|
|
||||||
[32m[1m[SUCCESS][0m Auth Challenge: PASS
|
|
||||||
[32m[1m[SUCCESS][0m Auth Flow: PASS
|
|
||||||
[32m[1m[SUCCESS][0m WebSocket AUTH: PASS
|
|
||||||
[32m[1m[SUCCESS][0m Configuration: PASS
|
|
||||||
[32m[1m[SUCCESS][0m Performance: PASS
|
|
||||||
[32m[1m[SUCCESS][0m Kind-Specific Auth: PASS
|
|
||||||
[32m[1m[SUCCESS][0m All NIP-42 tests completed successfully!
|
|
||||||
[32m[1m[SUCCESS][0m NIP-42 authentication implementation is working correctly
|
|
||||||
[34m[1m[INFO][0m === NIP-42 Authentication Tests Complete ===
|
|
||||||
677
tests/white_black_list_test.sh
Executable file
677
tests/white_black_list_test.sh
Executable file
@@ -0,0 +1,677 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# C-Relay Whitelist/Blacklist Authentication Rules Test Script
|
||||||
|
# =======================================================================
|
||||||
|
#
|
||||||
|
# This test validates the whitelist and blacklist functionality of the
|
||||||
|
# C-Relay server through the WebSocket admin API.
|
||||||
|
#
|
||||||
|
# Test Credentials (Test Mode):
|
||||||
|
# - Admin Private Key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
# - Admin Public Key: 6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3
|
||||||
|
# - Relay Public Key: 4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa
|
||||||
|
#
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# CONFIGURATION
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
# Test mode credentials (provided by user)
|
||||||
|
ADMIN_PRIVKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
|
ADMIN_PUBKEY="6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3"
|
||||||
|
RELAY_PUBKEY="4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"
|
||||||
|
|
||||||
|
# Server configuration
|
||||||
|
RELAY_HOST="localhost"
|
||||||
|
RELAY_PORT="8888"
|
||||||
|
RELAY_URL="ws://${RELAY_HOST}:${RELAY_PORT}"
|
||||||
|
|
||||||
|
# Test configuration
|
||||||
|
TIMEOUT=5
|
||||||
|
LOG_FILE="whitelist_blacklist_test.log"
|
||||||
|
TEMP_DIR="/tmp/c_relay_test_$$"
|
||||||
|
|
||||||
|
# Color codes for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
RESET='\033[0m'
|
||||||
|
|
||||||
|
# Test tracking
|
||||||
|
TESTS_RUN=0
|
||||||
|
TESTS_PASSED=0
|
||||||
|
TESTS_FAILED=0
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# UTILITY FUNCTIONS
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo -e "${BLUE}[$(date '+%H:%M:%S')]${RESET} $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${RESET} $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${RESET} $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${RESET} $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${RESET} $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
increment_test() {
|
||||||
|
TESTS_RUN=$((TESTS_RUN + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pass_test() {
|
||||||
|
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||||
|
log_success "Test $TESTS_RUN: PASSED - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
fail_test() {
|
||||||
|
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||||
|
log_error "Test $TESTS_RUN: FAILED - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate test keypairs
|
||||||
|
generate_test_keypair() {
|
||||||
|
local name=$1
|
||||||
|
local privkey_file="${TEMP_DIR}/${name}_privkey"
|
||||||
|
local pubkey_file="${TEMP_DIR}/${name}_pubkey"
|
||||||
|
|
||||||
|
# Generate private key using nak key --gen (following pattern from other tests)
|
||||||
|
local privkey=$(nak key generate 2>/dev/null)
|
||||||
|
if [ $? -ne 0 ] || [ -z "$privkey" ]; then
|
||||||
|
log_error "Failed to generate private key for $name"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$privkey" > "$privkey_file"
|
||||||
|
|
||||||
|
# Derive public key using nak
|
||||||
|
local pubkey=$(nak key public "$privkey" 2>/dev/null)
|
||||||
|
if [ $? -ne 0 ] || [ -z "$pubkey" ]; then
|
||||||
|
log_error "Failed to generate public key for $name"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$pubkey" > "$pubkey_file"
|
||||||
|
|
||||||
|
log_info "Generated keypair for $name: pubkey=${pubkey:0:16}..."
|
||||||
|
|
||||||
|
# Export for use in calling functions
|
||||||
|
eval "${name}_PRIVKEY=\"$privkey\""
|
||||||
|
eval "${name}_PUBKEY=\"$pubkey\""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send WebSocket message and capture response
|
||||||
|
send_websocket_message() {
|
||||||
|
local message="$1"
|
||||||
|
local expected_response="$2"
|
||||||
|
local timeout="${3:-$TIMEOUT}"
|
||||||
|
|
||||||
|
log_info "Sending WebSocket message: ${message:0:100}..."
|
||||||
|
|
||||||
|
# Use wscat to send message and capture response
|
||||||
|
local response=""
|
||||||
|
if command -v wscat &> /dev/null; then
|
||||||
|
response=$(echo "$message" | timeout "$timeout" wscat -c "$RELAY_URL" 2>/dev/null | head -1)
|
||||||
|
else
|
||||||
|
log_error "wscat not found - required for WebSocket testing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and send auth rule event
|
||||||
|
send_auth_rule_event() {
|
||||||
|
local action="$1" # "add" or "remove"
|
||||||
|
local rule_type="$2" # "whitelist" or "blacklist"
|
||||||
|
local pattern_type="$3" # "pubkey" or "hash"
|
||||||
|
local pattern_value="$4" # actual pubkey or hash value
|
||||||
|
local description="$5" # optional description
|
||||||
|
|
||||||
|
log_info "Creating auth rule event: $action $rule_type $pattern_type ${pattern_value:0:16}..."
|
||||||
|
|
||||||
|
# Create the auth rule event using nak - match the working NIP-42 pattern
|
||||||
|
local event_json
|
||||||
|
event_json=$(nak event -k 33335 --content "{\"action\":\"$action\",\"description\":\"$description\"}" \
|
||||||
|
-t "d=$RELAY_PUBKEY" \
|
||||||
|
-t "$rule_type=$pattern_type" \
|
||||||
|
-t "pattern=$pattern_value" \
|
||||||
|
-t "action=$action" \
|
||||||
|
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ $? -ne 0 ] || [ -z "$event_json" ]; then
|
||||||
|
log_error "Failed to create auth rule event with nak"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send the event using nak directly to relay (more reliable than wscat)
|
||||||
|
log_info "Publishing auth rule event to relay..."
|
||||||
|
local result
|
||||||
|
result=$(echo "$event_json" | timeout 10s nak event "$RELAY_URL" 2>&1)
|
||||||
|
local exit_code=$?
|
||||||
|
|
||||||
|
log_info "Auth rule event result: $result"
|
||||||
|
|
||||||
|
# Check if response indicates success
|
||||||
|
if [ $exit_code -eq 0 ] && echo "$result" | grep -q -i "success\|OK.*true\|published"; then
|
||||||
|
log_success "Auth rule $action successful"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Auth rule $action failed: $result (exit code: $exit_code)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test event publishing with a specific key
|
||||||
|
test_event_publishing() {
|
||||||
|
local test_privkey="$1"
|
||||||
|
local test_pubkey="$2"
|
||||||
|
local expected_result="$3" # "success" or "blocked"
|
||||||
|
local description="$4"
|
||||||
|
|
||||||
|
log_info "Testing event publishing: $description"
|
||||||
|
|
||||||
|
# Create a simple test event (kind 1 - text note) using nak like NIP-42 test
|
||||||
|
local test_content="Test message from ${test_pubkey:0:16}... at $(date)"
|
||||||
|
local test_event
|
||||||
|
test_event=$(nak event -k 1 --content "$test_content" --sec "$test_privkey" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ $? -ne 0 ] || [ -z "$test_event" ]; then
|
||||||
|
log_error "Failed to create test event"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send the event using nak directly (more reliable than wscat)
|
||||||
|
log_info "Publishing test event to relay..."
|
||||||
|
local result
|
||||||
|
result=$(echo "$test_event" | timeout 10s nak event "$RELAY_URL" 2>&1)
|
||||||
|
local exit_code=$?
|
||||||
|
|
||||||
|
log_info "Event publishing result: $result"
|
||||||
|
|
||||||
|
# Check result against expectation
|
||||||
|
if [ "$expected_result" = "success" ]; then
|
||||||
|
if [ $exit_code -eq 0 ] && echo "$result" | grep -q -i "success\|OK.*true\|published"; then
|
||||||
|
log_success "Event publishing allowed as expected"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Event publishing was blocked but should have been allowed: $result"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else # expected_result = "blocked"
|
||||||
|
if [ $exit_code -ne 0 ] || echo "$result" | grep -q -i "blocked\|denied\|rejected\|auth.*required\|OK.*false"; then
|
||||||
|
log_success "Event publishing blocked as expected"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Event publishing was allowed but should have been blocked: $result"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# SETUP AND INITIALIZATION
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
setup_test_environment() {
|
||||||
|
log "Setting up test environment..."
|
||||||
|
|
||||||
|
# Create temporary directory
|
||||||
|
mkdir -p "$TEMP_DIR"
|
||||||
|
|
||||||
|
# Clear log file
|
||||||
|
echo "=== C-Relay Whitelist/Blacklist Test Started at $(date) ===" > "$LOG_FILE"
|
||||||
|
|
||||||
|
# Check if required tools are available - like NIP-42 test
|
||||||
|
log_info "Checking dependencies..."
|
||||||
|
|
||||||
|
if ! command -v nak &> /dev/null; then
|
||||||
|
log_error "nak client not found. Please install: go install github.com/fiatjaf/nak@latest"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
log_error "jq not found. Please install jq for JSON processing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if ! command -v timeout &> /dev/null; then
|
||||||
|
log_error "timeout not found. Please install coreutils"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v wscat &> /dev/null; then
|
||||||
|
log_warning "wscat not found. Some WebSocket tests may be limited"
|
||||||
|
log_warning "Install with: npm install -g wscat"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Dependencies check complete"
|
||||||
|
|
||||||
|
# Generate test keypairs
|
||||||
|
generate_test_keypair "TEST1"
|
||||||
|
generate_test_keypair "TEST2"
|
||||||
|
generate_test_keypair "TEST3"
|
||||||
|
|
||||||
|
log_success "Test environment setup complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# TEST FUNCTIONS
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
# Test 1: Admin Authentication
|
||||||
|
test_admin_authentication() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Admin Authentication"
|
||||||
|
|
||||||
|
# Create a simple configuration event to test admin authentication
|
||||||
|
local content="Testing admin authentication"
|
||||||
|
local config_event
|
||||||
|
config_event=$(nak event -k 33334 --content "$content" \
|
||||||
|
-t "d=$RELAY_PUBKEY" \
|
||||||
|
-t "test_auth=true" \
|
||||||
|
--sec "$ADMIN_PRIVKEY" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
fail_test "Failed to create admin test event"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# DEBUG: Print the full event that will be sent
|
||||||
|
log_info "=== DEBUG: Full admin event being sent ==="
|
||||||
|
echo "$config_event" | jq . 2>/dev/null || echo "$config_event"
|
||||||
|
log_info "=== END DEBUG EVENT ==="
|
||||||
|
|
||||||
|
# Send admin event
|
||||||
|
local message="[\"EVENT\",$config_event]"
|
||||||
|
log_info "=== DEBUG: Full WebSocket message ==="
|
||||||
|
echo "$message"
|
||||||
|
log_info "=== END DEBUG MESSAGE ==="
|
||||||
|
|
||||||
|
local response
|
||||||
|
response=$(send_websocket_message "$message" "OK" 10)
|
||||||
|
|
||||||
|
# DEBUG: Print the full response from server
|
||||||
|
log_info "=== DEBUG: Full server response ==="
|
||||||
|
echo "$response"
|
||||||
|
log_info "=== END DEBUG RESPONSE ==="
|
||||||
|
|
||||||
|
if echo "$response" | grep -q '"OK".*true'; then
|
||||||
|
pass_test "Admin authentication successful"
|
||||||
|
else
|
||||||
|
fail_test "Admin authentication failed: $response"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 2: Basic Whitelist Functionality
|
||||||
|
test_basic_whitelist() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Basic Whitelist Functionality"
|
||||||
|
|
||||||
|
# Add TEST1 pubkey to whitelist
|
||||||
|
if send_auth_rule_event "add" "whitelist" "pubkey" "$TEST1_PUBKEY" "Test whitelist entry"; then
|
||||||
|
# Test that whitelisted pubkey can publish
|
||||||
|
if test_event_publishing "$TEST1_PRIVKEY" "$TEST1_PUBKEY" "success" "whitelisted pubkey"; then
|
||||||
|
pass_test "Basic whitelist functionality working"
|
||||||
|
else
|
||||||
|
fail_test "Whitelisted pubkey could not publish events"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Failed to add pubkey to whitelist"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 3: Basic Blacklist Functionality
|
||||||
|
test_basic_blacklist() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Basic Blacklist Functionality"
|
||||||
|
|
||||||
|
# Add TEST2 pubkey to blacklist
|
||||||
|
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST2_PUBKEY" "Test blacklist entry"; then
|
||||||
|
# Test that blacklisted pubkey cannot publish
|
||||||
|
if test_event_publishing "$TEST2_PRIVKEY" "$TEST2_PUBKEY" "blocked" "blacklisted pubkey"; then
|
||||||
|
pass_test "Basic blacklist functionality working"
|
||||||
|
else
|
||||||
|
fail_test "Blacklisted pubkey was able to publish events"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Failed to add pubkey to blacklist"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 4: Rule Removal
|
||||||
|
test_rule_removal() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Rule Removal"
|
||||||
|
|
||||||
|
# Remove TEST2 from blacklist
|
||||||
|
if send_auth_rule_event "remove" "blacklist" "pubkey" "$TEST2_PUBKEY" "Remove test blacklist entry"; then
|
||||||
|
# Test that previously blacklisted pubkey can now publish
|
||||||
|
if test_event_publishing "$TEST2_PRIVKEY" "$TEST2_PUBKEY" "success" "previously blacklisted pubkey after removal"; then
|
||||||
|
pass_test "Rule removal working correctly"
|
||||||
|
else
|
||||||
|
fail_test "Previously blacklisted pubkey still cannot publish after removal"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Failed to remove pubkey from blacklist"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 5: Multiple Users Scenario
|
||||||
|
test_multiple_users() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Multiple Users Scenario"
|
||||||
|
|
||||||
|
# Add TEST1 to whitelist and TEST3 to blacklist
|
||||||
|
local success_count=0
|
||||||
|
|
||||||
|
if send_auth_rule_event "add" "whitelist" "pubkey" "$TEST1_PUBKEY" "Multi-user test whitelist"; then
|
||||||
|
success_count=$((success_count + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST3_PUBKEY" "Multi-user test blacklist"; then
|
||||||
|
success_count=$((success_count + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $success_count -eq 2 ]; then
|
||||||
|
# Test whitelisted user can publish
|
||||||
|
if test_event_publishing "$TEST1_PRIVKEY" "$TEST1_PUBKEY" "success" "whitelisted in multi-user test"; then
|
||||||
|
# Test blacklisted user cannot publish
|
||||||
|
if test_event_publishing "$TEST3_PRIVKEY" "$TEST3_PUBKEY" "blocked" "blacklisted in multi-user test"; then
|
||||||
|
pass_test "Multiple users scenario working correctly"
|
||||||
|
else
|
||||||
|
fail_test "Blacklisted user in multi-user scenario was not blocked"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Whitelisted user in multi-user scenario was blocked"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Failed to set up multiple users scenario"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 6: Priority Testing (Blacklist vs Whitelist)
|
||||||
|
test_priority_rules() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Priority Rules Testing"
|
||||||
|
|
||||||
|
# Add same pubkey to both whitelist and blacklist
|
||||||
|
local setup_success=0
|
||||||
|
|
||||||
|
if send_auth_rule_event "add" "whitelist" "pubkey" "$TEST2_PUBKEY" "Priority test whitelist"; then
|
||||||
|
setup_success=$((setup_success + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST2_PUBKEY" "Priority test blacklist"; then
|
||||||
|
setup_success=$((setup_success + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $setup_success -eq 2 ]; then
|
||||||
|
# Test which rule takes priority (typically blacklist should win)
|
||||||
|
if test_event_publishing "$TEST2_PRIVKEY" "$TEST2_PUBKEY" "blocked" "pubkey in both whitelist and blacklist"; then
|
||||||
|
pass_test "Priority rules working correctly (blacklist takes precedence)"
|
||||||
|
else
|
||||||
|
# If whitelist wins, that's also valid depending on implementation
|
||||||
|
log_warning "Whitelist took precedence over blacklist - this may be implementation-specific"
|
||||||
|
pass_test "Priority rules working (whitelist precedence)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Failed to set up priority rules test"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 7: Hash-based Blacklist
|
||||||
|
test_hash_blacklist() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Hash-based Blacklist"
|
||||||
|
|
||||||
|
# Create a test event to get its hash
|
||||||
|
local test_content="Content to be blacklisted by hash"
|
||||||
|
local test_event
|
||||||
|
test_event=$(nak event -k 1 --content "$test_content" --sec "$TEST1_PRIVKEY" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ $? -ne 0 ] || [ -z "$test_event" ]; then
|
||||||
|
fail_test "Failed to create test event for hash blacklist"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract event ID (hash) from the event using jq
|
||||||
|
local event_id
|
||||||
|
event_id=$(echo "$test_event" | jq -r '.id' 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -z "$event_id" ] || [ "$event_id" = "null" ]; then
|
||||||
|
fail_test "Failed to extract event ID for hash blacklist test"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Testing hash blacklist with event ID: ${event_id:0:16}..."
|
||||||
|
|
||||||
|
# Add the event ID to hash blacklist
|
||||||
|
if send_auth_rule_event "add" "blacklist" "hash" "$event_id" "Test hash blacklist"; then
|
||||||
|
# Try to publish the same event using nak - should be blocked
|
||||||
|
log_info "Attempting to publish blacklisted event..."
|
||||||
|
local result
|
||||||
|
result=$(echo "$test_event" | timeout 10s nak event "$RELAY_URL" 2>&1)
|
||||||
|
local exit_code=$?
|
||||||
|
|
||||||
|
if [ $exit_code -ne 0 ] || echo "$result" | grep -q -i "blocked\|denied\|rejected\|blacklist"; then
|
||||||
|
pass_test "Hash-based blacklist working correctly"
|
||||||
|
else
|
||||||
|
fail_test "Hash-based blacklist did not block the event: $result"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Failed to add event hash to blacklist"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 8: WebSocket Connection Behavior
|
||||||
|
test_websocket_behavior() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: WebSocket Connection Behavior"
|
||||||
|
|
||||||
|
# Test that the WebSocket connection handles multiple rapid requests
|
||||||
|
local rapid_success_count=0
|
||||||
|
|
||||||
|
for i in {1..3}; do
|
||||||
|
local test_content="Rapid test message $i"
|
||||||
|
local test_event
|
||||||
|
test_event=$(nak event -k 1 --content "$test_content" --sec "$TEST1_PRIVKEY" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
local message="[\"EVENT\",$test_event]"
|
||||||
|
local response
|
||||||
|
response=$(send_websocket_message "$message" "OK" 5)
|
||||||
|
|
||||||
|
if echo "$response" | grep -q '"OK"'; then
|
||||||
|
rapid_success_count=$((rapid_success_count + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Small delay between requests
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $rapid_success_count -ge 2 ]; then
|
||||||
|
pass_test "WebSocket connection handles multiple requests correctly"
|
||||||
|
else
|
||||||
|
fail_test "WebSocket connection failed to handle multiple rapid requests ($rapid_success_count/3 succeeded)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 9: Rule Persistence Verification
|
||||||
|
test_rule_persistence() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Rule Persistence Verification"
|
||||||
|
|
||||||
|
# Add a rule, then verify it persists by testing enforcement
|
||||||
|
if send_auth_rule_event "add" "blacklist" "pubkey" "$TEST3_PUBKEY" "Persistence test blacklist"; then
|
||||||
|
# Wait a moment for rule to be processed
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Test enforcement multiple times to verify persistence
|
||||||
|
local enforcement_count=0
|
||||||
|
|
||||||
|
for i in {1..2}; do
|
||||||
|
if test_event_publishing "$TEST3_PRIVKEY" "$TEST3_PUBKEY" "blocked" "persistence test attempt $i"; then
|
||||||
|
enforcement_count=$((enforcement_count + 1))
|
||||||
|
fi
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $enforcement_count -eq 2 ]; then
|
||||||
|
pass_test "Rule persistence working correctly"
|
||||||
|
else
|
||||||
|
fail_test "Rule persistence failed ($enforcement_count/2 enforcements succeeded)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Failed to add rule for persistence test"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 10: Cleanup and Final Verification
|
||||||
|
test_cleanup_verification() {
|
||||||
|
increment_test
|
||||||
|
log "Test $TESTS_RUN: Cleanup and Final Verification"
|
||||||
|
|
||||||
|
# Remove all test rules
|
||||||
|
local cleanup_success=0
|
||||||
|
|
||||||
|
# Remove whitelist entries
|
||||||
|
if send_auth_rule_event "remove" "whitelist" "pubkey" "$TEST1_PUBKEY" "Cleanup whitelist"; then
|
||||||
|
cleanup_success=$((cleanup_success + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove blacklist entries
|
||||||
|
for pubkey in "$TEST2_PUBKEY" "$TEST3_PUBKEY"; do
|
||||||
|
if send_auth_rule_event "remove" "blacklist" "pubkey" "$pubkey" "Cleanup blacklist"; then
|
||||||
|
cleanup_success=$((cleanup_success + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $cleanup_success -ge 2 ]; then
|
||||||
|
# Verify that previously restricted pubkeys can now publish
|
||||||
|
if test_event_publishing "$TEST3_PRIVKEY" "$TEST3_PUBKEY" "success" "after cleanup verification"; then
|
||||||
|
pass_test "Cleanup and verification successful"
|
||||||
|
else
|
||||||
|
log_warning "Cleanup completed but restrictions may still be active"
|
||||||
|
pass_test "Cleanup completed (partial verification)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail_test "Cleanup failed ($cleanup_success rules removed)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# MAIN TEST EXECUTION
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
run_all_tests() {
|
||||||
|
log "Starting comprehensive whitelist/blacklist functionality tests..."
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
setup_test_environment
|
||||||
|
|
||||||
|
# Run only test 1 for debugging admin authentication
|
||||||
|
test_admin_authentication
|
||||||
|
|
||||||
|
# Comment out other tests for now to focus on debugging
|
||||||
|
# test_basic_whitelist
|
||||||
|
# test_basic_blacklist
|
||||||
|
# test_rule_removal
|
||||||
|
# test_multiple_users
|
||||||
|
# test_priority_rules
|
||||||
|
# test_hash_blacklist
|
||||||
|
# test_websocket_behavior
|
||||||
|
# test_rule_persistence
|
||||||
|
# test_cleanup_verification
|
||||||
|
|
||||||
|
# Test summary
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}=== TEST SUMMARY ===${RESET}"
|
||||||
|
echo -e "Tests run: ${BLUE}$TESTS_RUN${RESET}"
|
||||||
|
echo -e "Tests passed: ${GREEN}$TESTS_PASSED${RESET}"
|
||||||
|
echo -e "Tests failed: ${RED}$TESTS_FAILED${RESET}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $TESTS_FAILED -eq 0 ]; then
|
||||||
|
log_success "All tests passed! Whitelist/blacklist functionality is working correctly."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "$TESTS_FAILED out of $TESTS_RUN tests failed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# CLEANUP FUNCTIONS
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
log "Cleaning up test environment..."
|
||||||
|
|
||||||
|
# Remove temporary directory
|
||||||
|
if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
log_info "Temporary directory removed: $TEMP_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Test cleanup completed."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up cleanup trap
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# SCRIPT ENTRY POINT
|
||||||
|
# =======================================================================
|
||||||
|
|
||||||
|
main() {
|
||||||
|
echo -e "${BOLD}${BLUE}C-Relay Whitelist/Blacklist Authentication Test${RESET}"
|
||||||
|
echo -e "${BLUE}===============================================${RESET}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if relay is running - use the same method we verified manually
|
||||||
|
if ! echo '["REQ","connection_test",{}]' | timeout 5 wscat -c "$RELAY_URL" >/dev/null 2>&1; then
|
||||||
|
log_error "Cannot connect to relay at $RELAY_URL"
|
||||||
|
log_error "Please ensure the C-Relay server is running in test mode"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Connected to relay at $RELAY_URL"
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
if run_all_tests; then
|
||||||
|
echo ""
|
||||||
|
log_success "All whitelist/blacklist tests completed successfully!"
|
||||||
|
echo -e "Test log saved to: ${YELLOW}$LOG_FILE${RESET}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
log_error "Some tests failed. Check the log for details."
|
||||||
|
echo -e "Test log saved to: ${YELLOW}$LOG_FILE${RESET}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function if script is executed directly
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
main "$@"
|
||||||
|
fi
|
||||||
21
whitelist_blacklist_test.log
Normal file
21
whitelist_blacklist_test.log
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
=== C-Relay Whitelist/Blacklist Test Started at Tue Sep 23 11:20:40 AM EDT 2025 ===
|
||||||
|
[0;34m[INFO][0m Checking dependencies...
|
||||||
|
[0;32m[SUCCESS][0m Dependencies check complete
|
||||||
|
[0;34m[INFO][0m Generated keypair for TEST1: pubkey=eab7cac03049d07f...
|
||||||
|
[0;34m[INFO][0m Generated keypair for TEST2: pubkey=4e07a99f656d5301...
|
||||||
|
[0;34m[INFO][0m Generated keypair for TEST3: pubkey=bf48b836426805cb...
|
||||||
|
[0;32m[SUCCESS][0m Test environment setup complete
|
||||||
|
[0;34m[11:20:41][0m Test 1: Admin Authentication
|
||||||
|
[0;34m[INFO][0m === DEBUG: Full admin event being sent ===
|
||||||
|
[0;34m[INFO][0m === END DEBUG EVENT ===
|
||||||
|
[0;34m[INFO][0m === DEBUG: Full WebSocket message ===
|
||||||
|
[0;34m[INFO][0m === END DEBUG MESSAGE ===
|
||||||
|
[0;34m[INFO][0m Sending WebSocket message: ["EVENT",{"kind":33334,"id":"ce73fa326eb558505742770eb927a50edc16a69512089939f76da90c7ca5291f","pubk...
|
||||||
|
[0;34m[INFO][0m === DEBUG: Full server response ===
|
||||||
|
[0;34m[INFO][0m === END DEBUG RESPONSE ===
|
||||||
|
[0;31m[ERROR][0m Test 1: FAILED - Admin authentication failed: [0;34m[INFO][0m Sending WebSocket message: ["EVENT",{"kind":33334,"id":"ce73fa326eb558505742770eb927a50edc16a69512089939f76da90c7ca5291f","pubk...
|
||||||
|
[0;31m[ERROR][0m 1 out of 1 tests failed.
|
||||||
|
[0;31m[ERROR][0m Some tests failed. Check the log for details.
|
||||||
|
[0;34m[11:20:42][0m Cleaning up test environment...
|
||||||
|
[0;34m[INFO][0m Temporary directory removed: /tmp/c_relay_test_1773069
|
||||||
|
[0;34m[11:20:42][0m Test cleanup completed.
|
||||||
Reference in New Issue
Block a user