diff --git a/plinth/modules/security/__init__.py b/plinth/modules/security/__init__.py index 663751a17..14a5b8afd 100644 --- a/plinth/modules/security/__init__.py +++ b/plinth/modules/security/__init__.py @@ -113,8 +113,8 @@ def set_restricted_access(enabled): actions.superuser_run('security', [action]) -def get_vulnerability_counts(): - """Return number of security vulnerabilities for each app""" +def get_apps_report(): + """Return a security report for each app""" lines = subprocess.check_output(['debsecan']).decode().split('\n') cves = defaultdict(set) for line in lines: @@ -132,12 +132,12 @@ def get_vulnerability_counts(): 'freedombox': { 'name': 'freedombox', 'packages': {'freedombox'}, - 'count': 0, - 'past_count': 0 if past_cves else None, + 'vulns': 0, + 'past_vulns': 0 if past_cves else None, } } if past_cves and 'freedombox' in past_cves: - apps['freedombox']['past_count'] = len(past_cves['freedombox']) + apps['freedombox']['past_vulns'] = len(past_cves['freedombox']) for module_name, module in module_loader.loaded_modules.items(): try: @@ -145,6 +145,11 @@ def get_vulnerability_counts(): except AttributeError: continue # app has no managed packages + try: + services = module.managed_services + except AttributeError: + services = None + # filter out apps not setup yet if module.setup_helper.get_state() == 'needs-setup': continue @@ -152,17 +157,56 @@ def get_vulnerability_counts(): apps[module_name] = { 'name': module_name, 'packages': set(packages), - 'count': 0, - 'past_count': 0 if past_cves else None, + 'vulns': 0, + 'past_vulns': 0 if past_cves else None, + 'sandboxed': None, } for package in packages: if past_cves and package in past_cves: - apps[module_name]['past_count'] += len(past_cves[package]) + apps[module_name]['past_vulns'] += len(past_cves[package]) + + if services: + apps[module_name]['sandboxed'] = False + for service in services: + if _get_service_is_sandboxed(service): + apps[module_name]['sandboxed'] = True for cve_packages in cves.values(): for app_ in apps.values(): if cve_packages & app_['packages']: - app_['count'] += 1 + app_['vulns'] += 1 return apps + + +def _get_service_is_sandboxed(service): + """Return whether service is sandboxed.""" + lines = subprocess.check_output([ + 'systemctl', + 'show', + service, + '--property=ProtectSystem', + '--property=ProtectHome', + '--property=PrivateTmp', + '--property=PrivateDevices', + '--property=PrivateNetwork', + '--property=PrivateUsers', + '--property=PrivateMounts', + ]).decode().strip().split('\n') + pairs = [line.partition('=')[::2] for line in lines] + properties = {name: value for name, value in pairs} + if properties['ProtectSystem'] in ['yes', 'full', 'strict']: + return True + + if properties['ProtectHome'] in ['yes', 'read-only', 'tmpfs']: + return True + + for name in [ + 'PrivateTmp', 'PrivateDevices', 'PrivateNetwork', 'PrivateUsers', + 'PrivateMounts' + ]: + if properties[name] == 'yes': + return True + + return False diff --git a/plinth/modules/security/templates/security_report.html b/plinth/modules/security/templates/security_report.html index 19bbd001c..a1696b3d9 100644 --- a/plinth/modules/security/templates/security_report.html +++ b/plinth/modules/security/templates/security_report.html @@ -24,7 +24,7 @@ {% block content %}

{% trans "Security Report" %}

- {% blocktrans trimmed with count=freedombox_vulns.count %} + {% blocktrans trimmed with count=freedombox_report.vulns %} The installed version of FreedomBox has {{ count }} reported security vulnerabilities. {% endblocktrans %} @@ -32,7 +32,8 @@

{% blocktrans trimmed %} The following table lists the current reported number, and historical - count, of security vulnerabilities for each installed app. + count, of security vulnerabilities for each installed app. It also lists + whether each service is using sandboxing features. {% endblocktrans %}

@@ -41,14 +42,24 @@ + - {% for app in apps_vulns %} + {% for app in apps_report %} - - + + + {% endfor %} diff --git a/plinth/modules/security/views.py b/plinth/modules/security/views.py index 9e44d786c..d64eafde5 100644 --- a/plinth/modules/security/views.py +++ b/plinth/modules/security/views.py @@ -80,14 +80,13 @@ def _apply_changes(request, old_status, new_status): def report(request): """Serve the security report page""" - vulnerability_counts = security.get_vulnerability_counts() + apps_report = security.get_apps_report() return TemplateResponse( request, 'security_report.html', { 'title': _('Security Report'), - 'freedombox_vulns': - vulnerability_counts.pop('freedombox'), - 'apps_vulns': - sorted(vulnerability_counts.values(), - key=lambda app: app['name']), + 'freedombox_report': + apps_report.pop('freedombox'), + 'apps_report': + sorted(apps_report.values(), key=lambda app: app['name']), })
{% trans "App Name" %} {% trans "Current Vulnerabilities" %} {% trans "Past Vulnerabilities" %}{% trans "Sandboxed" %}
{{ app.name }}{{ app.count }}{{ app.past_count|default_if_none:"❗"}}{{ app.vulns }}{{ app.past_vulns|default_if_none:"❗"}} + {% if app.sandboxed is None %} + {% trans "N/A" %} + {% elif app.sandboxed %} + {% trans "Yes" %} + {% else %} + {% trans "No" %} + {% endif %} +