Rss feed for organization changes, translated RSS feeds, various bugfixes

parent 21178f8c
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-14 15:27+0000\n"
"POT-Creation-Date: 2017-12-15 12:27+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -19,9 +19,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: failmap_admin/map/static/js/failmap.js:68
#: failmap_admin/map/static/js/views.js:398
#: failmap_admin/map/static/js/views.js:403
#: failmap_admin/map/static/js/views.js:406
#: failmap_admin/map/static/js/views.js:404
#: failmap_admin/map/static/js/views.js:409
#: failmap_admin/map/static/js/views.js:412
msgid "View Full Screen"
msgstr ""
......@@ -337,63 +337,63 @@ msgstr ""
msgid "week"
msgstr ""
#: failmap_admin/map/static/js/views.js:178
#: failmap_admin/map/static/js/views.js:181
#: failmap_admin/map/static/js/views.js:184
#: failmap_admin/map/static/js/views.js:186
#: failmap_admin/map/static/js/views.js:188
#: failmap_admin/map/static/js/views.js:187
#: failmap_admin/map/static/js/views.js:190
#: failmap_admin/map/static/js/views.js:192
#: failmap_admin/map/static/js/views.js:194
msgid "Documentation"
msgstr ""
#: failmap_admin/map/static/js/views.js:179
#: failmap_admin/map/static/js/views.js:182
#: failmap_admin/map/static/js/views.js:185
#: failmap_admin/map/static/js/views.js:188
msgid "Second opinion"
msgstr ""
#: failmap_admin/map/static/js/views.js:204
#: failmap_admin/map/static/js/views.js:210
msgid "score perfect"
msgstr ""
#: failmap_admin/map/static/js/views.js:206
#: failmap_admin/map/static/js/views.js:212
msgid "score high"
msgstr ""
#: failmap_admin/map/static/js/views.js:208
#: failmap_admin/map/static/js/views.js:214
msgid "score medium"
msgstr ""
#: failmap_admin/map/static/js/views.js:210
#: failmap_admin/map/static/js/views.js:216
msgid "score low"
msgstr ""
#: failmap_admin/map/static/js/views.js:404
#: failmap_admin/map/static/js/views.js:410
msgid "Exit Full Screen"
msgstr ""
#: failmap_admin/map/static/js/views.js:435
#: failmap_admin/map/static/js/views.js:440
#: failmap_admin/map/static/js/views.js:441
#: failmap_admin/map/static/js/views.js:446
msgid "top congratulations"
msgstr ""
#: failmap_admin/map/static/js/views.js:436
#: failmap_admin/map/static/js/views.js:441
#: failmap_admin/map/static/js/views.js:442
#: failmap_admin/map/static/js/views.js:447
msgid "top position"
msgstr ""
#: failmap_admin/map/static/js/views.js:437
#: failmap_admin/map/static/js/views.js:443
msgid "top fail on failmap"
msgstr ""
#: failmap_admin/map/static/js/views.js:438
#: failmap_admin/map/static/js/views.js:443
#: failmap_admin/map/static/js/views.js:444
#: failmap_admin/map/static/js/views.js:449
msgid "hastag fail"
msgstr ""
#: failmap_admin/map/static/js/views.js:438
#: failmap_admin/map/static/js/views.js:443
#: failmap_admin/map/static/js/views.js:444
#: failmap_admin/map/static/js/views.js:449
msgid "hastag failmap"
msgstr ""
#: failmap_admin/map/static/js/views.js:442
#: failmap_admin/map/static/js/views.js:448
msgid "top win on failmap"
msgstr ""
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-14 15:27+0000\n"
"POT-Creation-Date: 2017-12-15 12:28+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -19,9 +19,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: failmap_admin/map/static/js/failmap.js:68
#: failmap_admin/map/static/js/views.js:398
#: failmap_admin/map/static/js/views.js:403
#: failmap_admin/map/static/js/views.js:406
#: failmap_admin/map/static/js/views.js:404
#: failmap_admin/map/static/js/views.js:409
#: failmap_admin/map/static/js/views.js:412
msgid "View Full Screen"
msgstr "Volledig scherm"
......@@ -235,7 +235,7 @@ msgstr "Onvoldoende versleuteld (F)"
#: failmap_admin/map/static/js/script.js:26
msgid "Certificate not valid for domain name."
msgstr "Certificaat niet geldig voor domeinnaam"
msgstr "Certificaat niet geldig voor domeinnaam."
#: failmap_admin/map/static/js/script.js:27
msgid ""
......@@ -346,64 +346,64 @@ msgstr "Strict-Transport-Security Header (HSTS)"
msgid "week"
msgstr "week"
#: failmap_admin/map/static/js/views.js:178
#: failmap_admin/map/static/js/views.js:181
#: failmap_admin/map/static/js/views.js:184
#: failmap_admin/map/static/js/views.js:186
#: failmap_admin/map/static/js/views.js:188
#: failmap_admin/map/static/js/views.js:187
#: failmap_admin/map/static/js/views.js:190
#: failmap_admin/map/static/js/views.js:192
#: failmap_admin/map/static/js/views.js:194
msgid "Documentation"
msgstr "Documentatie"
#: failmap_admin/map/static/js/views.js:179
#: failmap_admin/map/static/js/views.js:182
#: failmap_admin/map/static/js/views.js:185
#: failmap_admin/map/static/js/views.js:188
msgid "Second opinion"
msgstr "Second opinion"
#: failmap_admin/map/static/js/views.js:204
#: failmap_admin/map/static/js/views.js:210
msgid "score perfect"
msgstr "perfect"
#: failmap_admin/map/static/js/views.js:206
#: failmap_admin/map/static/js/views.js:212
msgid "score high"
msgstr "hoog"
#: failmap_admin/map/static/js/views.js:208
#: failmap_admin/map/static/js/views.js:214
msgid "score medium"
msgstr "midden"
#: failmap_admin/map/static/js/views.js:210
#: failmap_admin/map/static/js/views.js:216
msgid "score low"
msgstr "laag"
#: failmap_admin/map/static/js/views.js:404
#: failmap_admin/map/static/js/views.js:410
msgid "Exit Full Screen"
msgstr "Sluit volledig scherm"
#: failmap_admin/map/static/js/views.js:435
#: failmap_admin/map/static/js/views.js:440
#: failmap_admin/map/static/js/views.js:441
#: failmap_admin/map/static/js/views.js:446
msgid "top congratulations"
msgstr "gefeliciteerd"
#: failmap_admin/map/static/js/views.js:436
#: failmap_admin/map/static/js/views.js:441
#: failmap_admin/map/static/js/views.js:442
#: failmap_admin/map/static/js/views.js:447
msgid "top position"
msgstr "positie"
#: failmap_admin/map/static/js/views.js:437
#: failmap_admin/map/static/js/views.js:443
msgid "top fail on failmap"
msgstr "beste organisaties op faalkaart"
#: failmap_admin/map/static/js/views.js:438
#: failmap_admin/map/static/js/views.js:443
#: failmap_admin/map/static/js/views.js:444
#: failmap_admin/map/static/js/views.js:449
msgid "hastag fail"
msgstr "faal"
#: failmap_admin/map/static/js/views.js:438
#: failmap_admin/map/static/js/views.js:443
#: failmap_admin/map/static/js/views.js:444
#: failmap_admin/map/static/js/views.js:449
msgid "hastag failmap"
msgstr "faalkaart"
#: failmap_admin/map/static/js/views.js:442
#: failmap_admin/map/static/js/views.js:448
msgid "top win on failmap"
msgstr "beste organisaties op faalkaart"
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-14 15:27+0000\n"
"POT-Creation-Date: 2017-12-15 12:28+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -19,9 +19,9 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: failmap_admin/map/static/js/failmap.js:68
#: failmap_admin/map/static/js/views.js:398
#: failmap_admin/map/static/js/views.js:403
#: failmap_admin/map/static/js/views.js:406
#: failmap_admin/map/static/js/views.js:404
#: failmap_admin/map/static/js/views.js:409
#: failmap_admin/map/static/js/views.js:412
msgid "View Full Screen"
msgstr "🌈"
......@@ -337,64 +337,64 @@ msgstr "🌈"
msgid "week"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:178
#: failmap_admin/map/static/js/views.js:181
#: failmap_admin/map/static/js/views.js:184
#: failmap_admin/map/static/js/views.js:186
#: failmap_admin/map/static/js/views.js:188
#: failmap_admin/map/static/js/views.js:187
#: failmap_admin/map/static/js/views.js:190
#: failmap_admin/map/static/js/views.js:192
#: failmap_admin/map/static/js/views.js:194
msgid "Documentation"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:179
#: failmap_admin/map/static/js/views.js:182
#: failmap_admin/map/static/js/views.js:185
#: failmap_admin/map/static/js/views.js:188
msgid "Second opinion"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:204
#: failmap_admin/map/static/js/views.js:210
msgid "score perfect"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:206
#: failmap_admin/map/static/js/views.js:212
msgid "score high"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:208
#: failmap_admin/map/static/js/views.js:214
msgid "score medium"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:210
#: failmap_admin/map/static/js/views.js:216
msgid "score low"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:404
#: failmap_admin/map/static/js/views.js:410
msgid "Exit Full Screen"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:435
#: failmap_admin/map/static/js/views.js:440
#: failmap_admin/map/static/js/views.js:441
#: failmap_admin/map/static/js/views.js:446
msgid "top congratulations"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:436
#: failmap_admin/map/static/js/views.js:441
#: failmap_admin/map/static/js/views.js:442
#: failmap_admin/map/static/js/views.js:447
msgid "top position"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:437
#: failmap_admin/map/static/js/views.js:443
msgid "top fail on failmap"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:438
#: failmap_admin/map/static/js/views.js:443
#: failmap_admin/map/static/js/views.js:444
#: failmap_admin/map/static/js/views.js:449
msgid "hastag fail"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:438
#: failmap_admin/map/static/js/views.js:443
#: failmap_admin/map/static/js/views.js:444
#: failmap_admin/map/static/js/views.js:449
msgid "hastag failmap"
msgstr "🌈"
#: failmap_admin/map/static/js/views.js:442
#: failmap_admin/map/static/js/views.js:448
msgid "top win on failmap"
msgstr "🌈"
......
......@@ -10,36 +10,34 @@ logger = logging.getLogger(__package__)
def points_and_calculation(scan, scan_type):
if scan_type == "Strict-Transport-Security":
return security_headers_rating_based_on_scan(scan, scan_type)
if scan_type == "X-Content-Type-Options":
return security_headers_rating_based_on_scan(scan, scan_type)
if scan_type == "X-Frame-Options":
return security_headers_rating_based_on_scan(scan, scan_type)
if scan_type == "X-XSS-Protection":
return security_headers_rating_based_on_scan(scan, scan_type)
if scan_type == "plain_https":
return http_plain_rating_based_on_scan(scan)
if scan_type == "tls_qualys":
return tls_qualys_rating_based_on_scan(scan)
return 0, {}
# Can be probably more efficient by adding some methods to scan.
return calculation_methods[scan_type](scan)
def security_headers_rating_based_on_scan(scan, header='Strict-Transport-Security'):
security_headers_scores = {
# fasing out the header part. You can get this from the scan.
header = scan.type
"""
Rationale for classifcation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
# prevents reflected xss
'X-XSS-Protection': 100,
Classified as: Medium
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
# prevents clickjacking
'X-Frame-Options': 200,
Classified as: High
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
# forces the content-type to be leading for defining a type of file (not the browser guess)
# The browser guess could execute the file, for example with the wrong plugin.
# Basically the server admin should fix the browser, instead of the other way around.
'X-Content-Type-Options': 25,
Classified as: Low
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
# Will be the browser default. Forces https, even if http resources are available.
......@@ -52,6 +50,7 @@ def security_headers_rating_based_on_scan(scan, header='Strict-Transport-Securit
# governments have a once-a-year cycle for doing something requires. So HSTS should be
# longer than a year, like one year and three months. Some site punish long hsts times.
'Strict-Transport-Security': 200,
Classified as: High
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Public-Key-Pins
# Has the potential to make your site unreachable if not properly (automatically) maintained
......@@ -60,22 +59,27 @@ def security_headers_rating_based_on_scan(scan, header='Strict-Transport-Securit
# the transport.
# header likely to be killed like p3p
'Public-Key-Pins': 0,
Classified as: Ignored
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
# Very complex header to specify what resources can be loaded from where. Especially useful
# when loading in third party content such as horrible ads. Prevents against xss
'Content-Security-Policy': 50,
Classified as: Low
# Flash, PDF and other exploit prone things can be embedded. Should never happen:
# the content should always be none(?).
# if not set to none, it is 200 points for allowing flash and pdf to be embedded at all :)
'X-Permitted-Cross-Domain-Policies': 25,
Classified as: Ignored
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
# largely unsupported
# What referrer should be allowed to access the resource. Security on referrer headers? No.
'Referrer-Policy': 0
}
'Referrer-Policy':
Classified as: Ignored
todo: should be enabled(?)
"""
high = 0
medium = 0
......@@ -86,7 +90,8 @@ def security_headers_rating_based_on_scan(scan, header='Strict-Transport-Securit
points = 0
explanation = header + " header present."
else:
points = security_headers_scores[scan.type]
# points = security_headers_scores[scan.type]
points = 0
explanation = "Missing " + header + " header."
if header in ["X-Frame-Options", "Strict-Transport-Security"]:
......@@ -98,7 +103,7 @@ def security_headers_rating_based_on_scan(scan, header='Strict-Transport-Securit
calculation = {
"type": "security_headers_%s" % header.lower().replace("-", "_"),
"explanation": explanation,
"points": points,
"points": 0,
"since": scan.rating_determined_on.isoformat(),
"last_scan": scan.last_scan_moment.isoformat(),
"high": high,
......@@ -224,3 +229,14 @@ def tls_qualys_rating_based_on_scan(scan):
}
return gained_points, rating
# don't re-create the dict every time.
calculation_methods = {
'Strict-Transport-Security': security_headers_rating_based_on_scan,
'X-Content-Type-Options': security_headers_rating_based_on_scan,
'X-Frame-Options': security_headers_rating_based_on_scan,
'X-XSS-Protection': security_headers_rating_based_on_scan,
'plain_https': http_plain_rating_based_on_scan,
'tls_qualys': tls_qualys_rating_based_on_scan
}
......@@ -128,14 +128,14 @@ div#report {
background-image: repeating-linear-gradient(45deg, transparent, transparent 35px, rgba(255, 255, 255, .5) 35px, rgba(255, 255, 255, .5) 70px)
}
/* todo: consider making screenshots in aspect ratio? */
.clippedimage {
margin-top: 14px;
margin-left: 17px;
width: 320px; /* placeholder */
height: 200px; /* placeholder */
min-width: 320px; /* placeholder */
min-height: 200px; /* placeholder */
position: absolute;
clip: rect(0, 320px, 200px, 0);
border: 1px solid black;
/* If we can't find the image, would this then show? */
background-color: gainsboro;
......
{% load static %} {% load i18n %}
<!-- dynamic translates for the rss feed. See script.js for more examples for the javascript side of this. -->
{% trans "tls_qualys" %}
{% trans "plain_https" %}
{% trans "X-XSS-Protection" %}
{% trans "X-Frame-Options" %}
{% trans "X-Content-Type-Options" %}
{% trans "Strict-Transport-Security" %}
<!-- Sigh, the same translations as on the javascript side. That's what you get keeping the js translations short(er).
It doesn't do cross-file fuzzy, so you need to copy paste this and maintain these messages on multiple places.
Could we call the javascript library from somewhere if we want? Should we? -->
{% trans "Has a secure equivalent, which wasn't so in the past." %}
{% trans "Site does not redirect to secure url, and has no secure alternative on a standard port." %}
{% trans "Redirects to a secure site, while a secure counterpart on the standard port is missing." %}
{% trans "Broken Transport Security, rated F" %}
{% trans "Certificate not valid for domain name." %}
{% trans "Could not establish trust. For the certificate installation: Less than optimal Transport Security, rated C." %}
{% trans "Could not establish trust. For the certificate installation: Less than optimal Transport Security, rated B." %}
{% trans "Could not establish trust. For the certificate installation: Good Transport Security, rated A-." %}
{% trans "Could not establish trust. For the certificate installation: Good Transport Security, rated A." %}
{% trans "Less than optimal Transport Security, rated C." %}
{% trans "Less than optimal Transport Security, rated B." %}
{% trans "Good Transport Security, rated A-." %}
{% trans "Good Transport Security, rated A." %}
{% trans "Perfect Transport Security, rated A+." %}
{% trans "X-Content-Type-Options header present." %}
{% trans "Missing X-Content-Type-Options header." %}
{% trans "X-XSS-Protection header present." %}
{% trans "Missing X-XSS-Protection header." %}
{% trans "X-Frame-Options header present." %}
{% trans "Missing X-Frame-Options header." %}
{% trans "Strict-Transport-Security header present." %}
{% trans "Missing Strict-Transport-Security header." %}
\ No newline at end of file
......@@ -567,9 +567,10 @@
</div>
<br/>
<div v-if="name">
{% trans "Report share result question" %} {% verbatim %}<span
v-html="create_twitter_link(name, twitter_handle, points)"></span>{% endverbatim %}<br/>
<br/>
<p>{% trans "RSS change feed teaser" %}
<a :href="'/data/updates_on_organization_feed/' + selected"
target="_blank">{% trans "rss feed" %}</a>.</p>
{% trans "Data from" %}: {% verbatim %}{{ humanize(when) }}{% endverbatim %}<br/>
{% trans "Score" %}<span v-if="promise"><strong>*</strong></span>:<br/>
{% trans "High" %}: {% verbatim %}{{ high }}{% endverbatim %} <br/>
......@@ -577,6 +578,9 @@
{% trans "Low" %}: {% verbatim %}{{ low }}{% endverbatim %} <br/>
{% trans "congratulations" %}!
<br/>
{% trans "Report share result question" %} {% verbatim %}<span
v-html="create_twitter_link(name, twitter_handle, points)"></span>{% endverbatim %}<br/>
<br/>
<br/>
{% trans "Report ask pentest leader" %} <a
:href="'mailto:' + mailto + '?subject={% trans "Report ask pentest subject" %}' + name + '&body={% trans "Report ask pentest body" %}'">{% trans "Report ask request pentest" %}</a><br/>
......
......@@ -4,11 +4,13 @@ from django.conf import settings
from django.conf.urls import url
from django.views.i18n import JavaScriptCatalog
from failmap_admin.map.views import (LatestScanFeed, index, latest_scans, manifest_json, map_data,
organization_report, robots_txt, security_txt, stats,
terrible_urls, topfail, topwin, vulnstats, wanted_urls)
from failmap_admin.map.views import (LatestScanFeed, UpdatesOnOrganizationFeed, index, latest_scans,
manifest_json, map_data, organization_report, robots_txt,
security_txt, stats, terrible_urls, topfail, topwin,
updates_on_organization, vulnstats, wanted_urls)
urlpatterns = [
url(r'^$', index, name='failmap'),
url(r'^security.txt$', security_txt),
url(r'^robots.txt$', robots_txt),
url(r'^manifest.json$', manifest_json),
......@@ -26,7 +28,9 @@ urlpatterns = [
url(r'^data/wanted/', wanted_urls, name='wanted urls'),
url(r'^data/report/(?P<organization_id>[0-9]{0,200})/(?P<weeks_back>[0-9]{0,2})$',
organization_report, name='organization report'),
url(r'^$', index, name='failmap'),
url(r'^data/updates_on_organization/(?P<organization_id>[0-9]{1,6})$', updates_on_organization, name='asdf'),
url(r'^data/updates_on_organization_feed/(?P<organization_id>[0-9]{1,6})$', UpdatesOnOrganizationFeed()),
# proxy maptile requests, in production this can be done by caching proxy, this makes sure
# it works for dev. as well.
url(r'^proxy/(?P<url>https://api.tiles.mapbox.com/v4/.*.png$)',
......
......@@ -5,7 +5,9 @@ from datetime import datetime, timedelta
import pytz
from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.contrib.humanize.templatetags.humanize import naturaltime
from django.contrib.syndication.views import Feed
from django.core.exceptions import ObjectDoesNotExist
from django.db import connection
from django.db.models import Count
from django.http import JsonResponse
......@@ -1065,9 +1067,12 @@ def map_data(request, weeks_back=0):
return JsonResponse(data, encoder=JSEncoder)
def empty_response():
return JsonResponse({}, encoder=JSEncoder)
@cache_page(ten_minutes)
def latest_scans(request, scan_type):
from django.contrib.humanize.templatetags.humanize import naturaltime
scans = []
dataset = {
......@@ -1079,7 +1084,7 @@ def latest_scans(request, scan_type):
if scan_type not in ["tls_qualys",
"Strict-Transport-Security", "X-Content-Type-Options", "X-Frame-Options", "X-XSS-Protection",
"plain_https"]:
return JsonResponse(dataset, encoder=JSEncoder)
return empty_response()
if scan_type == "tls_qualys":
scans = list(TlsQualysScan.objects.order_by('-last_scan_moment')[0:6])
......@@ -1107,11 +1112,122 @@ def latest_scans(request, scan_type):
return JsonResponse(dataset, encoder=JSEncoder)
def latest_updates(organization_id):
"""
:param request:
:param organization_id: the id will always be "correct", whereas name will have all kinds of terribleness:
multiple organizations that have the same name in different branches, organizations with generic names etc.
Finding an organization by name is tricky. Therefore ID.
We're not filtering any further: given this might result in turning a blind eye to low or medium vulnerabilities.
:return:
"""
try:
# todo: is dead etc.
# todo: does this only do an exact match?
organization = Organization.objects.all().filter(pk=organization_id).get()
except ObjectDoesNotExist:
return empty_response()
dataset = {
"scans": [],
"render_date": datetime.now(pytz.utc).isoformat(),
"remark": remark,
}
# semi-union, given not all columns are the same. (not python/django-esque solution)
tls_scans = list(TlsQualysScan.objects.all().filter(
endpoint__url__organization=organization).order_by('-rating_determined_on')[0:10])
generic_endpoint_scans = list(EndpointGenericScan.objects.filter(
endpoint__url__organization=organization).order_by('-rating_determined_on')[0:60])
scans = tls_scans + generic_endpoint_scans
# todo: sort them, currently assumes the rss reader will do the sorting
# scans = sorted(scans, key=lambda k: k.last_scan_moment, reverse=False)
for scan in scans:
scan_type = getattr(scan, "type", "tls_qualys") # todo: should always be a property of scan
points, calculation = points_and_calculation(scan, scan_type)
dataset["scans"].append({
"organization": organization.name,
"organization_id": organization.pk,
"url": scan.endpoint.url.url,
"service": "%s/%s (IPv%s)" % (scan.endpoint.protocol, scan.endpoint.port, scan.endpoint.ip_version),
"protocol": scan.endpoint.protocol,
"port": scan.endpoint.port,
"ip_version": scan.endpoint.ip_version,
"scan_type": scan_type,
"explanation": calculation.get("explanation", ""), # sometimes you dont get one.
"high": calculation.get("high", 0),
"medium": calculation.get("medium", 0),
"low": calculation.get("low", 0),
"rating_determined_on_humanized": naturaltime(scan.rating_determined_on),
"rating_determined_on": scan.rating_determined_on,
"last_scan_humanized": naturaltime(scan.last_scan_moment),
"last_scan_moment": scan.last_scan_moment.isoformat()
})
return dataset
@cache_page(ten_minutes)
def updates_on_organization(request, organization_id):
if not organization_id:
return empty_response()
latest_updates(organization_id)
return JsonResponse(latest_updates(organization_id), encoder=JSEncoder)
class UpdatesOnOrganizationFeed(Feed):
link = "/data/updates_on_organization_feed/"
description = "Update feed."
def title(self, organization_id):
try:
organization = Organization.objects.all().filter(pk=organization_id).get()
except ObjectDoesNotExist:
return "Organization Updates"
return "%s Updates" % organization.name
# it seems weird to do this.
def get_object(self, request, *args, **kwargs):
return kwargs['organization_id']
# second parameter via magic
def items(self, organization_id):
return latest_updates(organization_id).get("scans", [])
def item_title(self, item):
rating = _("Perfect") if not any([item['high'], item['medium'], item['low']]) else \
_("High") if item['high'] else _("Medium") if item['medium'] else _("Low")
badge = "✅" if not any([item['high'], item['medium'], item['low']]) else \
"🔴" if item['high'] else "🔶" if item['medium'] else "🍋"
return "%s %s - %s: %s" % (badge, rating, item["url"], item["service"])
def item_description(self, item):
return "%s: %s" % (_(item["scan_type"]), _(item.get("explanation", "")))
def item_pubdate(self, item):
return item["rating_determined_on"]
# item_link is only needed if NewsItem has no get_absolute_url method.
# unique links are required to properly display a feed.
def item_link(self, item):
return "https://faalkaart.nl/#report-%s/%s/%s/%s" % \
(item["organization_id"], item["url"], item["service"], item["rating_determined_on"])
# @cache_page(ten_minutes), you can't cache this using the decorator.
class LatestScanFeed(Feed<