diff --git a/backend/device_registry/models.py b/backend/device_registry/models.py index 8ec0dd89d..863ac9970 100644 --- a/backend/device_registry/models.py +++ b/backend/device_registry/models.py @@ -105,13 +105,6 @@ class Device(models.Model): deb_packages_hash = models.CharField(max_length=32, blank=True) audit_files = JSONField(blank=True, default=list) - # TODO: to improve when we support saving full distro info. - def liable_for_vulnerable_packages_check(self): - if self.deb_packages_hash and self.deb_packages.exists() and \ - self.deb_packages.first().os_release_codename in DEBIAN_SUITES: - return True - return False - def sshd_issues(self): if self.audit_files: for file_info in self.audit_files: @@ -233,7 +226,7 @@ def hostname(self): def actions_count(self): return sum((self.deviceinfo.default_password is True, self.firewallstate.policy != FirewallState.POLICY_ENABLED_BLOCK, - self.vulnerable_packages().exists(), + bool(self.vulnerable_packages and self.vulnerable_packages.exists()), bool(self.insecure_services), bool(self.sshd_issues()))) @@ -309,8 +302,12 @@ def set_meta_tags(self): if self.deviceinfo.get_hardware_type() == 'Raspberry Pi' and raspberry_pi_tag not in self.tags: self.tags.add(raspberry_pi_tag) + @property def vulnerable_packages(self): - return self.deb_packages.filter(vulnerabilities__isnull=False).distinct().order_by('name') + if self.deb_packages_hash and self.deb_packages.exists() and \ + self.deb_packages.first().os_release_codename in DEBIAN_SUITES: + # FIXME: should use self.os_release_codename instead of a first deb package + return self.deb_packages.filter(vulnerabilities__isnull=False).distinct().order_by('name') class Meta: ordering = ('created',) diff --git a/backend/device_registry/templates/device_info_security.html b/backend/device_registry/templates/device_info_security.html index 0663d8003..6cacff6fb 100644 --- a/backend/device_registry/templates/device_info_security.html +++ b/backend/device_registry/templates/device_info_security.html @@ -23,22 +23,22 @@

Security

Vulnerable Packages - {% if object.liable_for_vulnerable_packages_check %} - {% with object.vulnerable_packages as vulnerable_packages %} - {% if vulnerable_packages %} - - {% endif %} - {% endwith %} - {% else %} + {% with object.vulnerable_packages as vulnerable_packages %} + {% if vulnerable_packages is None %} N/A + {% elif vulnerable_packages.exists %} + + {% else %} + {% include "badge.html" with icon="check" color="success" %} {% endif %} + {% endwith %} @@ -81,6 +81,8 @@

Security

{% endfor %} + {% else %} + N/A {% endif %} @@ -88,7 +90,9 @@

Security

Configuration Audit {% with object.sshd_issues as sshd_issues %} - {% if sshd_issues %} + {% if sshd_issues is None %} + N/A + {% elif sshd_issues %}
OpenSSH
+ {% else %} + {% include "badge.html" with icon="check" color="success" %} {% endif %} {% endwith %} diff --git a/backend/device_registry/tests/test_all.py b/backend/device_registry/tests/test_all.py index 646587b37..1bfb96c35 100644 --- a/backend/device_registry/tests/test_all.py +++ b/backend/device_registry/tests/test_all.py @@ -645,6 +645,53 @@ def test_insecure_services(self): {'name': 'fingerd', 'version': 'VERSION', 'arch': 'i386', 'os_release_codename': 'jessie'}]) + def test_vulnerable_packages_render(self): + self.client.login(username='test', password='123') + url = reverse('device-detail-security', kwargs={'pk': self.device.pk}) + + # No packages - should render N/A + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertInHTML(''' + + Vulnerable Packages + + + N/A + ''', response.rendered_content) + + self.device.deb_packages_hash = 'aabbccdd' + self.device.save() + self.device.set_deb_packages([ + {'name': 'python2', 'version': 'VERSION', 'source_name': 'python2', 'source_version': 'abcd', + 'arch': 'i386'}, + {'name': 'python3', 'version': 'VERSION', 'source_name': 'python3', 'source_version': 'abcd', + 'arch': 'i386'} + ], os_info={'codename': 'stretch'}) + # No vulnerable packages - green check mark + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertInHTML(''' + + Vulnerable Packages + + + + + + + ''', response.rendered_content) + + v = Vulnerability.objects.create(name='CVE-123', package='python2', is_binary=False, other_versions=[], + urgency=Vulnerability.Urgency.NONE, fix_available=True) + self.device.deb_packages.get(name='python2').vulnerabilities.add(v) + + # Has vulnerable packages - should render them + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'python2') + self.assertContains(response, 'CVE-123') + class PairingKeysView(TestCase): def setUp(self): diff --git a/backend/device_registry/views.py b/backend/device_registry/views.py index 96859dc99..1f8895757 100644 --- a/backend/device_registry/views.py +++ b/backend/device_registry/views.py @@ -517,7 +517,7 @@ def actions_view(request, device_pk=None): text_blocks = [] for dev in devices_with_vuln_packages: device_text_block = f'{dev.get_name()}' \ - f'({dev.vulnerable_packages().count()} packages)' + f'({dev.vulnerable_packages.count()} packages)' text_blocks.append(device_text_block) full_string = ', '.join(text_blocks) action = Action(