mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 05:44:38 +08:00
274 lines
7.9 KiB
Bash
Executable file
274 lines
7.9 KiB
Bash
Executable file
#!/bin/bash
|
|
# Security Features Verification Test
|
|
# Tests security features without requiring interactive SSH sessions
|
|
|
|
# Don't use set -e as we want to continue even if some commands fail
|
|
|
|
GREEN='\033[0;32m'
|
|
RED='\033[0;31m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
STATE_ROOT=$(mktemp -d "${TMPDIR:-/tmp}/tnt-security-test.XXXXXX")
|
|
SERVER_PIDS=""
|
|
NEXT_PORT=${PORT:-13600}
|
|
|
|
print_test() {
|
|
echo -e "\n${YELLOW}[TEST]${NC} $1"
|
|
}
|
|
|
|
pass() {
|
|
echo -e "${GREEN}✓ PASS${NC}: $1"
|
|
((PASS++))
|
|
}
|
|
|
|
fail() {
|
|
echo -e "${RED}✗ FAIL${NC}: $1"
|
|
((FAIL++))
|
|
}
|
|
|
|
cleanup() {
|
|
for pid in $SERVER_PIDS; do
|
|
kill "$pid" 2>/dev/null || true
|
|
wait "$pid" 2>/dev/null || true
|
|
done
|
|
rm -rf "$STATE_ROOT"
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
echo -e "${YELLOW}========================================${NC}"
|
|
echo -e "${YELLOW}TNT Security Features Test Suite${NC}"
|
|
echo -e "${YELLOW}========================================${NC}"
|
|
|
|
BIN="../tnt"
|
|
if [ ! -f "$BIN" ]; then
|
|
echo "Error: Binary $BIN not found."
|
|
exit 1
|
|
fi
|
|
|
|
run_server_probe() {
|
|
local name="$1"
|
|
local port="$NEXT_PORT"
|
|
local pid
|
|
local state_dir
|
|
local log_file
|
|
shift
|
|
|
|
NEXT_PORT=$((NEXT_PORT + 1))
|
|
state_dir="$STATE_ROOT/$name"
|
|
log_file="$state_dir/server.log"
|
|
mkdir -p "$state_dir"
|
|
|
|
"$@" "$BIN" -p "$port" -d "$state_dir" >"$log_file" 2>&1 &
|
|
pid=$!
|
|
SERVER_PIDS="$SERVER_PIDS $pid"
|
|
|
|
for _ in 1 2 3 4 5 6 7 8; do
|
|
if grep -q "TNT chat server listening" "$log_file"; then
|
|
kill "$pid" 2>/dev/null || true
|
|
wait "$pid" 2>/dev/null || true
|
|
return 0
|
|
fi
|
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
kill "$pid" 2>/dev/null || true
|
|
wait "$pid" 2>/dev/null || true
|
|
sed -n '1,120p' "$log_file"
|
|
return 1
|
|
}
|
|
|
|
# Test 1: 4096-bit RSA Key Generation
|
|
print_test "1. RSA 4096-bit Key Generation"
|
|
KEY_DIR="$STATE_ROOT/host-key"
|
|
|
|
if run_server_probe host-key env && [ -f "$KEY_DIR/host_key" ]; then
|
|
KEY_SIZE=$(ssh-keygen -l -f "$KEY_DIR/host_key" 2>/dev/null | awk '{print $1}')
|
|
if [ "$KEY_SIZE" = "4096" ]; then
|
|
pass "RSA key upgraded to 4096 bits (was 2048)"
|
|
else
|
|
fail "Key is $KEY_SIZE bits, expected 4096"
|
|
fi
|
|
|
|
# Check permissions
|
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
PERMS=$(stat -f "%OLp" "$KEY_DIR/host_key")
|
|
else
|
|
PERMS=$(stat -c "%a" "$KEY_DIR/host_key")
|
|
fi
|
|
|
|
if [ "$PERMS" = "600" ]; then
|
|
pass "Host key has secure permissions (600)"
|
|
else
|
|
fail "Host key permissions are $PERMS, expected 600"
|
|
fi
|
|
else
|
|
fail "Host key not generated"
|
|
fi
|
|
|
|
# Test 2: Server Start with Different Configurations
|
|
print_test "2. Environment Variable Configuration"
|
|
|
|
# Test bind address
|
|
run_server_probe bind-addr env TNT_BIND_ADDR=127.0.0.1 && \
|
|
pass "TNT_BIND_ADDR configuration works" || fail "TNT_BIND_ADDR not working"
|
|
|
|
# Test with access token set (just verify it starts)
|
|
run_server_probe access-token env TNT_ACCESS_TOKEN="test123" && \
|
|
pass "TNT_ACCESS_TOKEN configuration accepted" || fail "TNT_ACCESS_TOKEN not working"
|
|
|
|
# Test max connections configuration
|
|
run_server_probe max-connections env TNT_MAX_CONNECTIONS=10 && \
|
|
pass "TNT_MAX_CONNECTIONS configuration accepted" || fail "TNT_MAX_CONNECTIONS not working"
|
|
|
|
# Test per-IP connection rate configuration
|
|
run_server_probe conn-rate env TNT_MAX_CONN_RATE_PER_IP=20 && \
|
|
pass "TNT_MAX_CONN_RATE_PER_IP configuration accepted" || fail "TNT_MAX_CONN_RATE_PER_IP not working"
|
|
|
|
# Test rate limit toggle
|
|
run_server_probe rate-toggle env TNT_RATE_LIMIT=0 && \
|
|
pass "TNT_RATE_LIMIT configuration accepted" || fail "TNT_RATE_LIMIT not working"
|
|
|
|
sleep 1
|
|
|
|
# Test 3: Input Validation in Message Log
|
|
print_test "3. Message Log Sanitization"
|
|
MESSAGE_DIR="$STATE_ROOT/message-log"
|
|
mkdir -p "$MESSAGE_DIR"
|
|
|
|
# Create a test message log with potentially dangerous content
|
|
cat > "$MESSAGE_DIR/messages.log" <<EOF
|
|
2026-01-22T10:00:00Z|testuser|normal message
|
|
2026-01-22T10:01:00Z|user|with|pipes|attempt to break format
|
|
2026-01-22T10:02:00Z|user
|
|
newline|content with
|
|
newline
|
|
2026-01-22T10:03:00Z|validuser|valid content
|
|
EOF
|
|
|
|
# Start server and let it load messages, then verify it kept valid entries.
|
|
if run_server_probe message-log env >/dev/null &&
|
|
grep -q "validuser" "$MESSAGE_DIR/messages.log"; then
|
|
pass "Server loads messages from log file"
|
|
else
|
|
fail "Server message loading issue"
|
|
fi
|
|
|
|
# Test 4: UTF-8 Validation
|
|
print_test "4. UTF-8 Input Validation"
|
|
# Compile a small test program to verify UTF-8 validation
|
|
cat > test_utf8.c <<'EOF'
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include "../include/utf8.h"
|
|
#include "../include/common.h"
|
|
|
|
int main() {
|
|
// Valid UTF-8 sequences
|
|
char valid[] = {0xC3, 0xA9, 0}; // é
|
|
char invalid1[] = {0xC3, 0x28, 0}; // Invalid continuation byte
|
|
char overlong[] = {0xC0, 0x80, 0}; // Overlong encoding of NULL
|
|
|
|
if (utf8_is_valid_sequence(valid, 2)) {
|
|
printf("✓ Valid UTF-8 accepted\n");
|
|
} else {
|
|
printf("✗ Valid UTF-8 rejected\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!utf8_is_valid_sequence(invalid1, 2)) {
|
|
printf("✓ Invalid UTF-8 rejected\n");
|
|
} else {
|
|
printf("✗ Invalid UTF-8 accepted\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!utf8_is_valid_sequence(overlong, 2)) {
|
|
printf("✓ Overlong encoding rejected\n");
|
|
} else {
|
|
printf("✗ Overlong encoding accepted\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EOF
|
|
|
|
if gcc -I../include -o test_utf8 test_utf8.c ../src/utf8.c 2>/dev/null; then
|
|
if ./test_utf8; then
|
|
pass "UTF-8 validation function works correctly"
|
|
else
|
|
fail "UTF-8 validation has issues"
|
|
fi
|
|
rm -f test_utf8
|
|
else
|
|
echo " (Skipping UTF-8 test - compilation issue)"
|
|
fi
|
|
rm -f test_utf8.c
|
|
|
|
# Test 5: Buffer Safety with AddressSanitizer
|
|
print_test "5. Buffer Overflow Protection (ASAN Build)"
|
|
if make -C .. clean >/dev/null 2>&1 && make -C .. asan >/dev/null 2>&1; then
|
|
# Just verify it compiles - actual ASAN testing needs runtime
|
|
if [ -f ../tnt ]; then
|
|
pass "AddressSanitizer build successful"
|
|
# Restore normal build
|
|
make -C .. clean >/dev/null 2>&1 && make -C .. >/dev/null 2>&1
|
|
else
|
|
fail "AddressSanitizer build failed"
|
|
fi
|
|
else
|
|
echo " (Skipping ASAN test - build issue)"
|
|
fi
|
|
|
|
# Test 6: Concurrent Safety
|
|
print_test "6. Concurrency Safety (Data Structure Integrity)"
|
|
# This test verifies the code compiles with thread sanitizer flags
|
|
if gcc -fsanitize=thread -g -O1 -I../include -I/opt/homebrew/opt/libssh/include \
|
|
-c ../src/chat_room.c -o /tmp/test_tsan.o 2>/dev/null; then
|
|
pass "Code compiles with ThreadSanitizer (concurrency checks enabled)"
|
|
rm -f /tmp/test_tsan.o
|
|
else
|
|
fail "ThreadSanitizer compilation failed"
|
|
fi
|
|
|
|
# Test 7: Resource Management (Dynamic Allocation)
|
|
print_test "7. Resource Management (Large Log Files)"
|
|
LARGE_DIR="$STATE_ROOT/large-log"
|
|
mkdir -p "$LARGE_DIR"
|
|
# Create a large message log (2000 entries, more than old fixed 1000 limit)
|
|
for i in $(seq 1 2000); do
|
|
echo "2026-01-22T$(printf "%02d" $((i/100))):$(printf "%02d" $((i%60))):00Z|user$i|message $i" >> "$LARGE_DIR/messages.log"
|
|
done
|
|
|
|
# Check if server started successfully with large log
|
|
if run_server_probe large-log env >/dev/null && [ -f "$LARGE_DIR/messages.log" ]; then
|
|
LINE_COUNT=$(wc -l < "$LARGE_DIR/messages.log")
|
|
if [ "$LINE_COUNT" -ge 2000 ]; then
|
|
pass "Server handles large message log (${LINE_COUNT} messages)"
|
|
else
|
|
fail "Message log truncated unexpectedly"
|
|
fi
|
|
fi
|
|
|
|
# Summary
|
|
echo -e "\n${YELLOW}========================================${NC}"
|
|
echo -e "${YELLOW}Test Results${NC}"
|
|
echo -e "${YELLOW}========================================${NC}"
|
|
echo -e "${GREEN}Passed: $PASS${NC}"
|
|
echo -e "${RED}Failed: $FAIL${NC}"
|
|
echo -e "${YELLOW}========================================${NC}"
|
|
|
|
if [ $FAIL -eq 0 ]; then
|
|
echo -e "${GREEN}✓ All security features verified!${NC}"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}✗ Some tests failed${NC}"
|
|
exit 1
|
|
fi
|