diff --git a/.cursor/screenshots/page-2025-10-15T09-57-00-025Z.png b/.cursor/screenshots/page-2025-10-15T09-57-00-025Z.png
new file mode 100644
index 0000000..e81526a
Binary files /dev/null and b/.cursor/screenshots/page-2025-10-15T09-57-00-025Z.png differ
diff --git a/FILE_UPLOAD_SECURITY.md b/FILE_UPLOAD_SECURITY.md
new file mode 100644
index 0000000..0e3b248
--- /dev/null
+++ b/FILE_UPLOAD_SECURITY.md
@@ -0,0 +1,229 @@
+# File Upload Security Documentation
+
+## Overview
+
+The ivatar application now includes comprehensive file upload security features to protect against malicious file uploads, data leaks, and other security threats.
+
+## Security Features
+
+### 1. File Type Validation
+
+**Magic Bytes Verification**
+
+- Validates file signatures (magic bytes) to ensure uploaded files are actually images
+- Supports JPEG, PNG, GIF, WebP, BMP, and TIFF formats
+- Prevents file extension spoofing attacks
+
+**MIME Type Validation**
+
+- Uses python-magic library to detect actual MIME types
+- Cross-references with allowed MIME types list
+- Prevents MIME type confusion attacks
+
+### 2. Content Security Scanning
+
+**Malicious Content Detection**
+
+- Scans for embedded scripts (`'
+ self.large_data = b"x" * (10 * 1024 * 1024) # 10MB
+
+ def tearDown(self):
+ """Clean up after tests"""
+ pass
+
+ def test_valid_jpeg_validation(self):
+ """Test validation of valid JPEG file"""
+ validator = FileValidator(self.valid_jpeg_data, "test.jpg")
+ results = validator.comprehensive_validation()
+
+ self.assertTrue(results["valid"])
+ self.assertEqual(results["file_info"]["detected_type"], "image/jpeg")
+ self.assertGreaterEqual(results["security_score"], 80)
+
+ def test_magic_bytes_validation(self):
+ """Test magic bytes validation"""
+ validator = FileValidator(self.valid_jpeg_data, "test.jpg")
+ results = validator.validate_magic_bytes()
+
+ self.assertTrue(results["valid"])
+ self.assertEqual(results["detected_type"], "image/jpeg")
+
+ def test_malicious_content_detection(self):
+ """Test detection of malicious content"""
+ validator = FileValidator(self.malicious_data, "malicious.gif")
+ results = validator.scan_for_malicious_content()
+
+ self.assertTrue(results["suspicious"])
+ self.assertGreater(len(results["threats"]), 0)
+
+ def test_file_size_validation(self):
+ """Test file size validation"""
+ validator = FileValidator(self.large_data, "large.jpg")
+ results = validator.validate_basic()
+
+ self.assertFalse(results["valid"])
+ self.assertIn("File too large", results["errors"][0])
+
+ def test_invalid_extension_validation(self):
+ """Test invalid file extension validation"""
+ validator = FileValidator(self.valid_jpeg_data, "test.exe")
+ results = validator.validate_basic()
+
+ self.assertFalse(results["valid"])
+ self.assertIn("File extension not allowed", results["errors"][0])
+
+ def test_exif_sanitization(self):
+ """Test EXIF data sanitization"""
+ validator = FileValidator(self.valid_jpeg_data, "test.jpg")
+ sanitized_data = validator.sanitize_exif_data()
+
+ # Should return data (may be same or sanitized)
+ self.assertIsInstance(sanitized_data, bytes)
+ self.assertGreater(len(sanitized_data), 0)
+
+ def test_comprehensive_validation_function(self):
+ """Test the main validation function"""
+ is_valid, results, sanitized_data = validate_uploaded_file(
+ self.valid_jpeg_data, "test.jpg"
+ )
+
+ self.assertTrue(is_valid)
+ self.assertIsInstance(results, dict)
+ self.assertIsInstance(sanitized_data, bytes)
+
+ def test_security_report_generation(self):
+ """Test security report generation"""
+ report = get_file_security_report(self.valid_jpeg_data, "test.jpg")
+
+ self.assertIn("valid", report)
+ self.assertIn("security_score", report)
+ self.assertIn("file_info", report)
+
+ @patch("ivatar.file_security.magic.from_buffer")
+ def test_mime_type_validation(self, mock_magic):
+ """Test MIME type validation with mocked magic"""
+ mock_magic.return_value = "image/jpeg"
+
+ validator = FileValidator(self.valid_jpeg_data, "test.jpg")
+ results = validator.validate_mime_type()
+
+ self.assertTrue(results["valid"])
+ self.assertEqual(results["detected_mime"], "image/jpeg")
+
+ def test_polyglot_attack_detection(self):
+ """Test detection of polyglot attacks"""
+ polyglot_data = b'GIF89a'
+ validator = FileValidator(polyglot_data, "polyglot.gif")
+ results = validator.scan_for_malicious_content()
+
+ self.assertTrue(results["suspicious"])
+ self.assertIn("polyglot attack", results["threats"][0].lower())
+
+
+class UploadPhotoFormSecurityTestCase(TestCase):
+ """Test cases for UploadPhotoForm security enhancements"""
+
+ def setUp(self):
+ """Set up test data"""
+ self.user = User.objects.create_user(
+ username="testuser", email="test@example.com", password="testpass123"
+ )
+
+ def test_form_validation_with_valid_file(self):
+ """Test form validation with valid file"""
+ valid_jpeg_data = b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c\x1c $.' \",#\x1c\x1c(7),01444\x1f'9=82<.342\xff\xc0\x00\x11\x08\x00\x01\x00\x01\x01\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\x07\"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17\x18\x19\x1a%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x3f\x00\xf9\xff\xd9"
+
+ uploaded_file = SimpleUploadedFile(
+ "test.jpg", valid_jpeg_data, content_type="image/jpeg"
+ )
+
+ form_data = {"photo": uploaded_file, "not_porn": True, "can_distribute": True}
+
+ form = UploadPhotoForm(data=form_data, files={"photo": uploaded_file})
+
+ # Mock the validation to avoid PIL issues in tests
+ with patch("ivatar.file_security.validate_uploaded_file") as mock_validate:
+ mock_validate.return_value = (True, {"security_score": 95}, valid_jpeg_data)
+
+ self.assertTrue(form.is_valid())
+
+ def test_form_validation_with_malicious_file(self):
+ """Test form validation with malicious file"""
+ malicious_data = b'GIF89a'
+
+ uploaded_file = SimpleUploadedFile(
+ "malicious.gif", malicious_data, content_type="image/gif"
+ )
+
+ form_data = {"photo": uploaded_file, "not_porn": True, "can_distribute": True}
+
+ form = UploadPhotoForm(data=form_data, files={"photo": uploaded_file})
+
+ # Mock the validation to return malicious file detection
+ with patch("ivatar.file_security.validate_uploaded_file") as mock_validate:
+ mock_validate.return_value = (
+ False,
+ {"security_score": 20, "errors": ["Malicious content detected"]},
+ malicious_data,
+ )
+
+ self.assertFalse(form.is_valid())
+ self.assertIn("malicious", str(form.errors["photo"]))
+
+
+class UploadPhotoViewSecurityTestCase(TestCase):
+ """Test cases for UploadPhotoView security enhancements"""
+
+ def setUp(self):
+ """Set up test data"""
+ self.user = User.objects.create_user(
+ username="testuser", email="test@example.com", password="testpass123"
+ )
+
+ def tearDown(self):
+ """Clean up after tests"""
+ pass
+
+
+@override_settings(
+ ENABLE_FILE_SECURITY_VALIDATION=True,
+ ENABLE_EXIF_SANITIZATION=True,
+ ENABLE_MALICIOUS_CONTENT_SCAN=True,
+ ENABLE_RATE_LIMITING=True,
+)
+class FileSecurityIntegrationTestCase(TestCase):
+ """Integration tests for file upload security"""
+
+ def setUp(self):
+ """Set up test data"""
+ self.user = User.objects.create_user(
+ username="testuser", email="test@example.com", password="testpass123"
+ )
+
+ def test_end_to_end_security_validation(self):
+ """Test end-to-end security validation"""
+ # This would test the complete flow from upload to storage
+ # with all security checks enabled
+ pass
+
+ def test_security_logging(self):
+ """Test that security events are properly logged"""
+ # This would test that security events are logged
+ # when malicious files are uploaded
+ pass
diff --git a/requirements.txt b/requirements.txt
index 538f724..005e722 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -34,6 +34,7 @@ pymemcache
PyMySQL
python-coveralls
python-language-server
+python-magic>=0.4.27
pytz
rope
setuptools