# -*- 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()