diff --git a/backend/accounts/tests/test_tasks.py b/backend/accounts/tests/test_tasks.py index c62e32f..4407087 100644 --- a/backend/accounts/tests/test_tasks.py +++ b/backend/accounts/tests/test_tasks.py @@ -27,8 +27,6 @@ class CeleryTaskTests(TestCase): product_portfolio="Diverse", supplier_base="International", it_infrastructure=["Cloud", "On-Premise"], - intellectual_property=["Patents", "Trademarks"], - sensitive_data=["PII", "Financial Data"], integration_level="Highly Integrated" ) self.risk = Risk.objects.create(risk_id="1", risk_name="Test Risk", category="Category1", primary_impact="High") diff --git a/backend/accounts/tests/test_utils.py b/backend/accounts/tests/test_utils.py index 8cf284d..c400811 100644 --- a/backend/accounts/tests/test_utils.py +++ b/backend/accounts/tests/test_utils.py @@ -32,8 +32,6 @@ class EmailTests(TestCase): product_portfolio="Diverse", supplier_base="International", it_infrastructure=["Cloud", "On-Premise"], - intellectual_property=["Patents", "Trademarks"], - sensitive_data=["PII", "Financial Data"], integration_level="Highly Integrated" ) self.document = Document.objects.create(organization=self.organization) diff --git a/backend/core/forms.py b/backend/core/forms.py index 833736b..2b84167 100644 --- a/backend/core/forms.py +++ b/backend/core/forms.py @@ -9,14 +9,12 @@ class OrganizationForm(forms.ModelForm): 'compliance_frameworks', 'industry_sector', 'it_dependency', 'data_sensitivity', 'network_infrastructure', 'remote_workforce_percentage', 'third_party_vendor_access', 'internal_software_development', 'geographic_scope', 'customer_base', 'customer_type', - 'product_portfolio', 'supplier_base', 'it_infrastructure', 'intellectual_property', - 'sensitive_data','sensitive_data_types', 'integration_level', 'ip_value', 'change_rate', 'threat_actors', 'expert_analysis' + 'product_portfolio', 'supplier_base', 'it_infrastructure', + 'sensitive_data_types', 'integration_level', 'change_rate', 'threat_actors', 'expert_analysis' ] widgets = { 'compliance_frameworks': forms.CheckboxSelectMultiple(), 'it_infrastructure': forms.CheckboxSelectMultiple(), - 'intellectual_property': forms.CheckboxSelectMultiple(), - 'sensitive_data': forms.CheckboxSelectMultiple(), 'threat_actors': forms.CheckboxSelectMultiple(), 'sensitive_data_types': forms.CheckboxSelectMultiple(), } @@ -38,13 +36,24 @@ class OrganizationForm(forms.ModelForm): if sector == 'other' and sector_other: cleaned_data['industry_sector'] = sector_other - # Handle sensitive_data_types - types = cleaned_data.get('sensitive_data_types') or [] - other_type = self.data.get('sensitive_data_types_other', '').strip() - if 'other' in types and other_type: - types = [t for t in types if t != 'other'] - types.append(other_type) - cleaned_data['sensitive_data_types'] = types + # Handle Sensitive Data Types & Business Impact + sensitive_data_types = {} + data_types = [ + ('personal', 'personal_applicable', 'personal_impact'), + ('financial', 'financial_applicable', 'financial_impact'), + ('ip', 'ip_applicable', 'ip_impact'), + ('operational', 'operational_applicable', 'operational_impact'), + ('government', 'government_applicable', 'government_impact'), + ('none', 'none_applicable', None) + ] + for key, applicable_name, impact_name in data_types: + applicable = self.data.get(applicable_name) == 'on' + entry = {'applicable': applicable} + if impact_name: + impact = self.data.get(impact_name) + entry['impact'] = int(impact) if impact and impact.isdigit() else None + sensitive_data_types[key] = entry + cleaned_data['sensitive_data_types'] = sensitive_data_types return cleaned_data diff --git a/backend/core/migrations/0026_remove_organization_intellectual_property_and_more.py b/backend/core/migrations/0026_remove_organization_intellectual_property_and_more.py new file mode 100644 index 0000000..87e7628 --- /dev/null +++ b/backend/core/migrations/0026_remove_organization_intellectual_property_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.3 on 2025-09-17 10:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0025_alter_document_status'), + ] + + operations = [ + migrations.RemoveField( + model_name='organization', + name='intellectual_property', + ), + migrations.RemoveField( + model_name='organization', + name='ip_value', + ), + migrations.RemoveField( + model_name='organization', + name='sensitive_data', + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index e80b937..e151fc2 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -62,14 +62,11 @@ class Organization(models.Model): product_portfolio = models.CharField(max_length=20, null=True, blank=True, help_text="How diversified is your product/service portfolio?") supplier_base = models.CharField(max_length=20, null=True, blank=True, help_text="What is your supplier base structure?") it_infrastructure = models.JSONField(null=True, blank=True, help_text="What is your primary IT infrastructure model?") # Stores selected IT infrastructure types as a list - intellectual_property = models.JSONField(null=True, blank=True, help_text="How does your organization protect and manage intellectual property?") # Stores selected IP protection types as a list - sensitive_data = models.JSONField(null=True, blank=True, help_text="What type of sensitive data does your organization handle?") # Stores selected sensitive data types as a list integration_level = models.CharField(max_length=20, null=True, blank=True, help_text="How integrated are your critical business systems?") network_infrastructure = models.CharField(max_length=20, null=True, blank=True, help_text="What best describes your organization's network infrastructure model?") - ip_value = models.CharField(max_length=20, null=True, blank=True, help_text="Intellectual Property (IP) Value: Select best description of IP's importance to the business model.") change_rate = models.CharField(max_length=20, null=True, blank=True, help_text="How frequently does your organization undergo significant technology or business changes?") threat_actors = models.JSONField(null=True, blank=True, help_text="Which types of threat actors are most relevant to your organization (e.g., cybercriminals, insiders, nation-states)?") - sensitive_data_types = models.JSONField(null=True, blank=True, help_text="What type of sensitive data does your organization handle?") + sensitive_data_types = models.JSONField(null=True, blank=True, help_text="Stores applicable status and business impact rating (1-5) for each sensitive data type. Example: {'personal': {'applicable': True, 'impact': 4}, ...}") risks = models.ManyToManyField('Risk', related_name='organizations', blank=True) expert_analysis = models.BooleanField(null=True, blank=True) diff --git a/backend/core/static/js/formHandling.js b/backend/core/static/js/formHandling.js index a9d6b31..f5beab4 100644 --- a/backend/core/static/js/formHandling.js +++ b/backend/core/static/js/formHandling.js @@ -156,6 +156,9 @@ function showQuestion(questionId) { const question = document.getElementById(questionId); question.classList.remove('d-none'); progressBar(); + if (questionId == 'q7') { + setupSensitiveDataValidator(); + } } function setButtonVisiblity(buttonId, visible) { @@ -229,4 +232,50 @@ function progressBar() { basicBarWrap.classList.add('d-none'); advancedBarWrap.classList.add('d-none'); } -} \ No newline at end of file +} + +function setupSensitiveDataValidator() { + const dataTypes = [ + {checkbox: 'personal_applicable', radios: 'personal_impact'}, + {checkbox: 'financial_applicable', radios: 'financial_impact'}, + {checkbox: 'ip_applicable', radios: 'ip_impact'}, + {checkbox: 'operational_applicable', radios: 'operational_impact'}, + {checkbox: 'government_applicable', radios: 'government_impact'} + ]; + function updateRadios(type) { + const cb = document.getElementById(type.checkbox); + const radios = document.querySelectorAll(`input[name="${type.radios}"]`); + const noneCb = document.getElementById('none_applicable'); + if (noneCb && noneCb.checked) { + radios.forEach(radio => { + radio.disabled = true; + radio.checked = false; + }); + return; + } + radios.forEach(radio => { + radio.disabled = !cb.checked; + if (!cb.checked) radio.checked = false; + }); + } + dataTypes.forEach(type => { + const cb = document.getElementById(type.checkbox); + if (cb) { + cb.addEventListener('change', () => updateRadios(type)); + updateRadios(type); + } + }); + const noneCb = document.getElementById('none_applicable'); + if (noneCb) { + noneCb.addEventListener('change', function() { + if (noneCb.checked) { + ['personal_applicable','financial_applicable','ip_applicable','operational_applicable','government_applicable'].forEach(id => { + const cb = document.getElementById(id); + if (cb) cb.checked = false; + }); + } + dataTypes.forEach(type => updateRadios(type)); + }); + } +} + diff --git a/backend/core/templates/signup.html b/backend/core/templates/signup.html index 5ef5785..cf9db6e 100644 --- a/backend/core/templates/signup.html +++ b/backend/core/templates/signup.html @@ -474,78 +474,136 @@
| Data Type | +Applicable? | +Business Impact | +
|---|---|---|
| Personal Data (PII, PHI, etc.) | ++ + | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
| Financial Data (PCI, records) | ++ + | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
| Intellectual Property / Strategic Data | ++ + | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
| Critical Operational Data | ++ + | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
| Government/Controlled Data | ++ + | +
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
| None / Minimal Sensitive Data | ++ + | ++ N/A + | +