# -*- coding: utf-8 -*- """ Security and Validation Tests for Survey Custom Certificate Template This test module verifies the security features and validation logic implemented in Task 15. """ import json import unittest from odoo.tests.common import TransactionCase from odoo.exceptions import ValidationError, UserError class TestSecurityValidation(TransactionCase): """Test security and validation features.""" def setUp(self): super(TestSecurityValidation, self).setUp() # Create a test survey self.survey = self.env['survey.survey'].create({ 'title': 'Test Security Survey', 'access_mode': 'public', }) def test_placeholder_key_validation(self): """Test that invalid placeholder keys are rejected.""" wizard = self.env['survey.custom.certificate.wizard'].create({ 'survey_id': self.survey.id, }) # Test valid placeholder key valid_placeholder = self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.valid_field}', 'value_type': 'custom_text', }) self.assertTrue(valid_placeholder.id, "Valid placeholder should be created") # Test invalid placeholder key - missing braces with self.assertRaises(ValidationError): self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': 'key.invalid', 'value_type': 'custom_text', }) # Test invalid placeholder key - special characters with self.assertRaises(ValidationError): self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.invalid-field}', 'value_type': 'custom_text', }) # Test invalid placeholder key - SQL injection attempt with self.assertRaises(ValidationError): self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': "{key.field'; DROP TABLE users--}", 'value_type': 'custom_text', }) def test_value_field_validation(self): """Test that invalid value_field names are rejected.""" wizard = self.env['survey.custom.certificate.wizard'].create({ 'survey_id': self.survey.id, }) # Test valid value_field valid_placeholder = self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.test}', 'value_type': 'survey_field', 'value_field': 'survey_title', }) self.assertTrue(valid_placeholder.id, "Valid value_field should be accepted") # Test valid value_field with dots valid_placeholder2 = self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.test2}', 'value_type': 'user_field', 'value_field': 'partner_id.name', }) self.assertTrue(valid_placeholder2.id, "Valid value_field with dots should be accepted") # Test invalid value_field - special characters with self.assertRaises(ValidationError): self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.test3}', 'value_type': 'survey_field', 'value_field': 'field-name', }) # Test invalid value_field - SQL injection attempt with self.assertRaises(ValidationError): self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.test4}', 'value_type': 'survey_field', 'value_field': "field'; DROP TABLE--", }) def test_custom_text_length_validation(self): """Test that excessively long custom text is rejected.""" wizard = self.env['survey.custom.certificate.wizard'].create({ 'survey_id': self.survey.id, }) # Test valid custom text valid_placeholder = self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.test}', 'value_type': 'custom_text', 'custom_text': 'Valid text', }) self.assertTrue(valid_placeholder.id, "Valid custom text should be accepted") # Test custom text at limit (1000 characters) long_text = 'x' * 1000 valid_long = self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.test2}', 'value_type': 'custom_text', 'custom_text': long_text, }) self.assertTrue(valid_long.id, "Custom text at limit should be accepted") # Test custom text exceeding limit too_long_text = 'x' * 1001 with self.assertRaises(ValidationError): self.env['survey.certificate.placeholder'].create({ 'wizard_id': wizard.id, 'source_key': '{key.test3}', 'value_type': 'custom_text', 'custom_text': too_long_text, }) def test_json_mappings_validation(self): """Test that invalid JSON mappings are rejected.""" # Test valid JSON mappings valid_mappings = json.dumps({ 'placeholders': [ { 'key': '{key.name}', 'value_type': 'user_field', 'value_field': 'partner_name', 'custom_text': '', } ] }) survey = self.env['survey.survey'].create({ 'title': 'Test JSON Survey', 'custom_cert_mappings': valid_mappings, 'has_custom_certificate': True, }) self.assertTrue(survey.id, "Valid JSON mappings should be accepted") # Test invalid JSON - not a dictionary with self.assertRaises(ValidationError): self.env['survey.survey'].create({ 'title': 'Test Invalid JSON 1', 'custom_cert_mappings': '["not", "a", "dict"]', 'has_custom_certificate': True, }) # Test invalid JSON - missing placeholders key with self.assertRaises(ValidationError): self.env['survey.survey'].create({ 'title': 'Test Invalid JSON 2', 'custom_cert_mappings': '{"wrong_key": []}', 'has_custom_certificate': True, }) # Test invalid JSON - invalid placeholder key format invalid_key_mappings = json.dumps({ 'placeholders': [ { 'key': 'invalid_key', 'value_type': 'custom_text', 'value_field': '', 'custom_text': 'test', } ] }) with self.assertRaises(ValidationError): self.env['survey.survey'].create({ 'title': 'Test Invalid JSON 3', 'custom_cert_mappings': invalid_key_mappings, 'has_custom_certificate': True, }) # Test invalid JSON - invalid value_type invalid_type_mappings = json.dumps({ 'placeholders': [ { 'key': '{key.test}', 'value_type': 'invalid_type', 'value_field': '', 'custom_text': '', } ] }) with self.assertRaises(ValidationError): self.env['survey.survey'].create({ 'title': 'Test Invalid JSON 4', 'custom_cert_mappings': invalid_type_mappings, 'has_custom_certificate': True, }) def test_sanitization_methods(self): """Test that sanitization methods work correctly.""" from ..wizards.survey_custom_certificate_wizard import SurveyCustomCertificateWizard # Test HTML escaping html_input = '' sanitized = SurveyCustomCertificateWizard._sanitize_placeholder_value(html_input) self.assertNotIn('', sanitized, "HTML tags should be removed/escaped") # Test control character removal control_chars = 'test\x00\x01\x02string' sanitized = SurveyCustomCertificateWizard._sanitize_placeholder_value(control_chars) self.assertEqual(sanitized, 'teststring', "Control characters should be removed") # Test length limiting very_long = 'x' * 20000 sanitized = SurveyCustomCertificateWizard._sanitize_placeholder_value(very_long) self.assertLessEqual(len(sanitized), 10000, "Long strings should be truncated") # Test empty/None values self.assertEqual( SurveyCustomCertificateWizard._sanitize_placeholder_value(None), '', "None should return empty string" ) self.assertEqual( SurveyCustomCertificateWizard._sanitize_placeholder_value(''), '', "Empty string should return empty string" ) def test_placeholder_key_format_validation(self): """Test the static placeholder key validation method.""" from ..wizards.survey_custom_certificate_wizard import SurveyCustomCertificateWizard # Valid keys self.assertTrue( SurveyCustomCertificateWizard._validate_placeholder_key('{key.name}') ) self.assertTrue( SurveyCustomCertificateWizard._validate_placeholder_key('{key.field_name}') ) self.assertTrue( SurveyCustomCertificateWizard._validate_placeholder_key('{key.field123}') ) # Invalid keys self.assertFalse( SurveyCustomCertificateWizard._validate_placeholder_key('key.name') ) self.assertFalse( SurveyCustomCertificateWizard._validate_placeholder_key('{key.name') ) self.assertFalse( SurveyCustomCertificateWizard._validate_placeholder_key('key.name}') ) self.assertFalse( SurveyCustomCertificateWizard._validate_placeholder_key('{key.field-name}') ) self.assertFalse( SurveyCustomCertificateWizard._validate_placeholder_key('{key.field name}') ) self.assertFalse( SurveyCustomCertificateWizard._validate_placeholder_key('') ) self.assertFalse( SurveyCustomCertificateWizard._validate_placeholder_key(None) ) def test_json_structure_validation_method(self): """Test the static JSON structure validation method.""" from ..wizards.survey_custom_certificate_wizard import SurveyCustomCertificateWizard # Valid JSON valid_json = json.dumps({ 'placeholders': [ { 'key': '{key.test}', 'value_type': 'custom_text', 'value_field': '', 'custom_text': 'test', } ] }) is_valid, error = SurveyCustomCertificateWizard._validate_json_structure(valid_json) self.assertTrue(is_valid, f"Valid JSON should pass: {error}") # Invalid JSON - syntax error is_valid, error = SurveyCustomCertificateWizard._validate_json_structure('{invalid}') self.assertFalse(is_valid, "Invalid JSON syntax should fail") # Invalid JSON - not a dict is_valid, error = SurveyCustomCertificateWizard._validate_json_structure('[]') self.assertFalse(is_valid, "JSON array should fail") # Invalid JSON - missing placeholders key is_valid, error = SurveyCustomCertificateWizard._validate_json_structure('{}') self.assertFalse(is_valid, "Missing placeholders key should fail") # Invalid JSON - empty string is_valid, error = SurveyCustomCertificateWizard._validate_json_structure('') self.assertFalse(is_valid, "Empty string should fail") if __name__ == '__main__': unittest.main()