315 lines
11 KiB
Bash
Executable File
315 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Script to test transaction immutability on active database (from .env)
|
|
# This script safely tests database permissions without altering real data
|
|
#
|
|
# Tests:
|
|
# 1. Can we INSERT into transactions? (should be allowed)
|
|
# 2. Can we UPDATE transactions? (should be DENIED)
|
|
# 3. Can we DELETE transactions? (should be DENIED)
|
|
#
|
|
# Safety: Uses database transactions with ROLLBACK to prevent any data changes
|
|
|
|
set -e # Exit on error
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}Transaction Immutability Test${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo ""
|
|
|
|
# Get database credentials from .env
|
|
if [ ! -f .env ]; then
|
|
echo -e "${RED}Error: .env file not found${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
DB_DATABASE=$(grep "^DB_DATABASE=" .env | cut -d '=' -f2 | sed 's/#.*//' | tr -d '"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/\r$//')
|
|
DB_USERNAME=$(grep "^DB_USERNAME=" .env | cut -d '=' -f2 | sed 's/#.*//' | tr -d '"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/\r$//')
|
|
DB_PASSWORD=$(grep "^DB_PASSWORD=" .env | cut -d '=' -f2- | sed 's/#.*//' | sed 's/^"//' | sed 's/"$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/\r$//')
|
|
DB_HOST=$(grep "^DB_HOST=" .env | cut -d '=' -f2 | sed 's/#.*//' | tr -d '"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/\r$//')
|
|
|
|
# Default to localhost if not set
|
|
if [ -z "$DB_HOST" ]; then
|
|
DB_HOST="localhost"
|
|
fi
|
|
|
|
echo -e "${BLUE}Database:${NC} $DB_DATABASE"
|
|
echo -e "${BLUE}User:${NC} $DB_USERNAME"
|
|
echo -e "${BLUE}Host:${NC} $DB_HOST"
|
|
echo ""
|
|
|
|
# Check if mysql command is available
|
|
if ! command -v mysql &> /dev/null; then
|
|
echo -e "${RED}Error: mysql command not found${NC}"
|
|
echo "Please install mysql-client"
|
|
exit 1
|
|
fi
|
|
|
|
# Test database connection
|
|
echo -e "${YELLOW}Testing database connection...${NC}"
|
|
if ! MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" -e "SELECT 1;" 2>/dev/null; then
|
|
echo -e "${RED}Error: Cannot connect to database${NC}"
|
|
echo -e "${YELLOW}Debug info:${NC}"
|
|
echo -e " Database: $DB_DATABASE"
|
|
echo -e " Username: $DB_USERNAME"
|
|
echo -e " Host: $DB_HOST"
|
|
exit 1
|
|
fi
|
|
echo -e "${GREEN}✓ Database connection successful${NC}"
|
|
echo ""
|
|
|
|
# Get sample account IDs for testing
|
|
echo -e "${YELLOW}Getting sample account IDs...${NC}"
|
|
ACCOUNT_IDS=$(MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" -N -e "SELECT id FROM accounts LIMIT 2;")
|
|
FROM_ACCOUNT=$(echo "$ACCOUNT_IDS" | head -n 1)
|
|
TO_ACCOUNT=$(echo "$ACCOUNT_IDS" | tail -n 1)
|
|
|
|
if [ -z "$FROM_ACCOUNT" ] || [ -z "$TO_ACCOUNT" ]; then
|
|
echo -e "${RED}Error: Could not find sample accounts${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${GREEN}✓ Using account IDs: $FROM_ACCOUNT → $TO_ACCOUNT${NC}"
|
|
echo ""
|
|
|
|
# Create a temporary test transaction ID variable
|
|
TEST_TRANSACTION_ID=""
|
|
|
|
# ====================
|
|
# TEST 1: INSERT (should be ALLOWED)
|
|
# ====================
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}TEST 1: INSERT Permission${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
|
|
MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" <<EOF
|
|
START TRANSACTION;
|
|
|
|
-- Try to insert a test transaction
|
|
INSERT INTO transactions (
|
|
from_account_id,
|
|
to_account_id,
|
|
amount,
|
|
description,
|
|
transaction_type_id,
|
|
created_at,
|
|
updated_at
|
|
) VALUES (
|
|
$FROM_ACCOUNT,
|
|
$TO_ACCOUNT,
|
|
1,
|
|
'IMMUTABILITY TEST - WILL BE ROLLED BACK',
|
|
1,
|
|
NOW(),
|
|
NOW()
|
|
);
|
|
|
|
-- Get the ID of the inserted test transaction
|
|
SELECT @test_id := LAST_INSERT_ID();
|
|
|
|
-- Display the test transaction
|
|
SELECT id, from_account_id, to_account_id, amount, description
|
|
FROM transactions
|
|
WHERE id = @test_id;
|
|
|
|
-- ROLLBACK to prevent any data changes
|
|
ROLLBACK;
|
|
|
|
SELECT 'Transaction ROLLED BACK - no data was changed' AS status;
|
|
EOF
|
|
|
|
INSERT_RESULT=$?
|
|
|
|
if [ $INSERT_RESULT -eq 0 ]; then
|
|
echo -e "${GREEN}✓ INSERT permission: ALLOWED${NC}"
|
|
echo -e "${GREEN} Database user CAN create new transactions${NC}"
|
|
else
|
|
echo -e "${RED}✗ INSERT permission: DENIED${NC}"
|
|
echo -e "${RED} Database user CANNOT create transactions${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
# ====================
|
|
# TEST 2: UPDATE (should be DENIED)
|
|
# ====================
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}TEST 2: UPDATE Permission${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
|
|
# Get an existing transaction ID
|
|
EXISTING_ID=$(MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" -N -e "SELECT id FROM transactions ORDER BY id DESC LIMIT 1;")
|
|
|
|
if [ -z "$EXISTING_ID" ]; then
|
|
echo -e "${YELLOW}No existing transactions found, skipping UPDATE test${NC}"
|
|
UPDATE_RESULT=1
|
|
else
|
|
echo -e "Testing UPDATE on transaction ID: $EXISTING_ID"
|
|
|
|
# Get original amount
|
|
ORIGINAL_AMOUNT=$(MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" -N -e "SELECT amount FROM transactions WHERE id = $EXISTING_ID;")
|
|
echo -e "Original amount: $ORIGINAL_AMOUNT"
|
|
|
|
# Try to update (wrapped in transaction for safety)
|
|
MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" <<EOF 2>&1 | tee /tmp/update_test.log
|
|
START TRANSACTION;
|
|
|
|
-- Try to update an existing transaction
|
|
UPDATE transactions
|
|
SET amount = 99999, description = 'IMMUTABILITY TEST - SHOULD BE BLOCKED'
|
|
WHERE id = $EXISTING_ID;
|
|
|
|
-- Check if update succeeded
|
|
SELECT amount, description
|
|
FROM transactions
|
|
WHERE id = $EXISTING_ID;
|
|
|
|
-- ROLLBACK for safety
|
|
ROLLBACK;
|
|
EOF
|
|
|
|
UPDATE_RESULT=${PIPESTATUS[0]}
|
|
|
|
# Verify the transaction was not modified
|
|
CURRENT_AMOUNT=$(MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" -N -e "SELECT amount FROM transactions WHERE id = $EXISTING_ID;")
|
|
|
|
if [ "$CURRENT_AMOUNT" != "$ORIGINAL_AMOUNT" ]; then
|
|
echo -e "${RED}✗ UPDATE permission: ALLOWED (CRITICAL SECURITY ISSUE)${NC}"
|
|
echo -e "${RED} Transaction amount was changed from $ORIGINAL_AMOUNT to $CURRENT_AMOUNT${NC}"
|
|
echo -e "${RED} ⚠️ TRANSACTIONS ARE MUTABLE - THIS IS A CRITICAL FINANCIAL SECURITY ISSUE${NC}"
|
|
elif grep -q "denied" /tmp/update_test.log 2>/dev/null; then
|
|
echo -e "${GREEN}✓ UPDATE permission: DENIED${NC}"
|
|
echo -e "${GREEN} Database user CANNOT modify transactions (secure)${NC}"
|
|
else
|
|
# Update command succeeded but amount unchanged (transaction rolled back)
|
|
echo -e "${YELLOW}⚠ UPDATE permission: ALLOWED but transaction rolled back${NC}"
|
|
echo -e "${YELLOW} Database user HAS UPDATE permission (potential security issue)${NC}"
|
|
echo -e "${YELLOW} Data was not changed due to ROLLBACK${NC}"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# ====================
|
|
# TEST 3: DELETE (should be DENIED)
|
|
# ====================
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}TEST 3: DELETE Permission${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
|
|
if [ -z "$EXISTING_ID" ]; then
|
|
echo -e "${YELLOW}No existing transactions found, skipping DELETE test${NC}"
|
|
DELETE_RESULT=1
|
|
else
|
|
echo -e "Testing DELETE on transaction ID: $EXISTING_ID"
|
|
|
|
# Verify transaction exists
|
|
EXISTS_BEFORE=$(MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" -N -e "SELECT COUNT(*) FROM transactions WHERE id = $EXISTING_ID;")
|
|
echo -e "Transaction exists: $EXISTS_BEFORE"
|
|
|
|
# Try to delete (wrapped in transaction for safety)
|
|
MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" <<EOF 2>&1 | tee /tmp/delete_test.log
|
|
START TRANSACTION;
|
|
|
|
-- Try to delete an existing transaction
|
|
DELETE FROM transactions WHERE id = $EXISTING_ID;
|
|
|
|
-- Check if delete succeeded
|
|
SELECT COUNT(*) as remaining
|
|
FROM transactions
|
|
WHERE id = $EXISTING_ID;
|
|
|
|
-- ROLLBACK for safety
|
|
ROLLBACK;
|
|
EOF
|
|
|
|
DELETE_RESULT=${PIPESTATUS[0]}
|
|
|
|
# Verify the transaction still exists
|
|
EXISTS_AFTER=$(MYSQL_PWD="$DB_PASSWORD" mysql -h"$DB_HOST" -u"$DB_USERNAME" "$DB_DATABASE" -N -e "SELECT COUNT(*) FROM transactions WHERE id = $EXISTING_ID;")
|
|
|
|
if [ "$EXISTS_AFTER" != "$EXISTS_BEFORE" ]; then
|
|
echo -e "${RED}✗ DELETE permission: ALLOWED (CRITICAL SECURITY ISSUE)${NC}"
|
|
echo -e "${RED} Transaction was deleted${NC}"
|
|
echo -e "${RED} ⚠️ TRANSACTIONS CAN BE DELETED - THIS IS A CRITICAL FINANCIAL SECURITY ISSUE${NC}"
|
|
elif grep -q "denied" /tmp/delete_test.log 2>/dev/null; then
|
|
echo -e "${GREEN}✓ DELETE permission: DENIED${NC}"
|
|
echo -e "${GREEN} Database user CANNOT delete transactions (secure)${NC}"
|
|
else
|
|
# Delete command succeeded but transaction still exists (rolled back)
|
|
echo -e "${YELLOW}⚠ DELETE permission: ALLOWED but transaction rolled back${NC}"
|
|
echo -e "${YELLOW} Database user HAS DELETE permission (potential security issue)${NC}"
|
|
echo -e "${YELLOW} Data was not changed due to ROLLBACK${NC}"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# ====================
|
|
# FINAL SUMMARY
|
|
# ====================
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE}SUMMARY${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo ""
|
|
|
|
ISSUES_FOUND=0
|
|
|
|
if [ $INSERT_RESULT -eq 0 ]; then
|
|
echo -e "${GREEN}✓ INSERT: ALLOWED (expected)${NC}"
|
|
else
|
|
echo -e "${RED}✗ INSERT: DENIED (unexpected - should be allowed)${NC}"
|
|
ISSUES_FOUND=$((ISSUES_FOUND + 1))
|
|
fi
|
|
|
|
# Check UPDATE test results
|
|
if grep -q "denied" /tmp/update_test.log 2>/dev/null; then
|
|
echo -e "${GREEN}✓ UPDATE: DENIED (expected - secure)${NC}"
|
|
elif [ $UPDATE_RESULT -ne 0 ]; then
|
|
echo -e "${GREEN}✓ UPDATE: DENIED (expected - secure)${NC}"
|
|
else
|
|
echo -e "${RED}✗ UPDATE: ALLOWED (security issue)${NC}"
|
|
ISSUES_FOUND=$((ISSUES_FOUND + 1))
|
|
fi
|
|
|
|
# Check DELETE test results
|
|
if grep -q "denied" /tmp/delete_test.log 2>/dev/null; then
|
|
echo -e "${GREEN}✓ DELETE: DENIED (expected - secure)${NC}"
|
|
elif [ $DELETE_RESULT -ne 0 ]; then
|
|
echo -e "${GREEN}✓ DELETE: DENIED (expected - secure)${NC}"
|
|
else
|
|
echo -e "${RED}✗ DELETE: ALLOWED (security issue)${NC}"
|
|
ISSUES_FOUND=$((ISSUES_FOUND + 1))
|
|
fi
|
|
|
|
echo ""
|
|
|
|
if [ $ISSUES_FOUND -eq 0 ]; then
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN}✓ ALL TESTS PASSED${NC}"
|
|
echo -e "${GREEN}Transaction immutability is properly enforced${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
else
|
|
echo -e "${RED}========================================${NC}"
|
|
echo -e "${RED}✗ $ISSUES_FOUND SECURITY ISSUE(S) FOUND${NC}"
|
|
echo -e "${RED}Transaction immutability is NOT properly enforced${NC}"
|
|
echo -e "${RED}========================================${NC}"
|
|
echo ""
|
|
echo -e "${YELLOW}Recommended Fix:${NC}"
|
|
echo -e "${YELLOW}REVOKE UPDATE, DELETE ON $DB_DATABASE.transactions FROM '$DB_USERNAME'@'$DB_HOST';${NC}"
|
|
echo -e "${YELLOW}FLUSH PRIVILEGES;${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${BLUE}Note: All tests used database transactions with ROLLBACK${NC}"
|
|
echo -e "${BLUE}No actual data was modified in the database${NC}"
|
|
|
|
# Cleanup
|
|
rm -f /tmp/update_test.log /tmp/delete_test.log
|
|
|
|
exit $ISSUES_FOUND
|