From 89f1e6e62c90a8979538e3e28ee6a6a77c78c141 Mon Sep 17 00:00:00 2001
From: Phil Elson
Date: Fri, 6 Nov 2020 23:32:25 +0100
Subject: [PATCH 1/2] Use the Jinja templating engine to remove pre-formatting
values.
---
cara/apps/calculator/report_generator.py | 32 +++----------
cara/apps/calculator/templates/report.html.j2 | 46 +++++++++----------
2 files changed, 30 insertions(+), 48 deletions(-)
diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py
index d0a3f7e6..78789faa 100644
--- a/cara/apps/calculator/report_generator.py
+++ b/cara/apps/calculator/report_generator.py
@@ -69,7 +69,7 @@ def plot(times, concentrations):
return fig
-def minutes_to_string(minutes: int) -> str:
+def minutes_to_time(minutes: int) -> str:
minute_string = str(minutes % 60)
minute_string = "0" * (2 - len(minute_string)) + minute_string
hour_string = str(minutes // 60)
@@ -88,34 +88,16 @@ def build_report(model: models.Model, form: FormData):
'request': request,
'form': form,
'creation_date': time,
- 'model_version': 'Beta v1.0.0',
- 'simulation_name': form.simulation_name,
- 'room_number': form.room_number,
- 'room_volume': form.room_volume,
- 'air_supply': form.air_supply,
- 'air_changes': form.air_changes,
- 'total_people': form.total_people,
- 'infected_people': form.infected_people,
- 'activity_type': form.activity_type,
- 'activity_start': minutes_to_string(form.activity_start),
- 'activity_finish': minutes_to_string(form.activity_finish),
- 'infected_start': minutes_to_string(form.infected_start),
- 'infected_finish': minutes_to_string(form.infected_finish),
- 'event_type': form.event_type,
- 'single_event_date': form.single_event_date,
- 'recurrent_event_month': form.recurrent_event_month,
- 'lunch_option': form.lunch_option,
- 'lunch_start': minutes_to_string(form.lunch_start),
- 'lunch_finish': minutes_to_string(form.lunch_finish),
- 'coffee_breaks': form.coffee_breaks,
- 'coffee_duration': form.coffee_duration,
- 'coffee_times': [[minutes_to_string(start), minutes_to_string(finish)] for start, finish in form.coffee_break_times()],
- 'mask_wearing': form.mask_wearing,
+ 'model_version': 'Beta v1.0.0',
}
context.update(calculate_report_data(model))
p = Path(__file__).parent / "templates"
- env = jinja2.Environment(loader=jinja2.FileSystemLoader(Path(p)))
+ env = jinja2.Environment(
+ loader=jinja2.FileSystemLoader(Path(p)),
+ undefined=jinja2.StrictUndefined,
+ )
+ env.filters['minutes_to_time'] = minutes_to_time
template = env.get_template("report.html.j2")
return template.render(**context)
\ No newline at end of file
diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2
index c21f81e4..9f18d3f9 100644
--- a/cara/apps/calculator/templates/report.html.j2
+++ b/cara/apps/calculator/templates/report.html.j2
@@ -13,8 +13,8 @@
Created {{ creation_date }} using model version {{ model_version }}
- Simulation Name: {{ simulation_name }}
- Room Number: {{ room_number }}
+ Simulation Name: {{ form.simulation_name }}
+ Room Number: {{ form.room_number }}
Input data:
@@ -29,9 +29,9 @@
@@ -58,51 +58,51 @@
Event data:
- Number of attendees and infected people: {{ total_people }} in attendance, of whom {{ infected_people }}
- {{ "is" if infected_people == 1 else "are" }}
+
Number of attendees and infected people: {{ form.total_people }} in attendance, of whom {{ form.infected_people }}
+ {{ "is" if form.infected_people == 1 else "are" }}
infected.
Activity type:
- {% if activity_type == "office" %}
+ {% if form.activity_type == "office" %}
Office work – typical scenario with all persons seated, talking.
- {% elif activity_type == "workshop" %}
+ {% elif form.activity_type == "workshop" %}
Workshop = assembly workshop environment, all persons doing light exercise, talking.
- {% elif activity_type == "training" %}
+ {% elif form.activity_type == "training" %}
Training – one person (the trainer) standing, talking, all others seated, talking quietly (whispering). It is assumed the trainer is the infected person, for the worst case scenario.
{% endif %}
Exposure time (presence of infected person):
- {% if event_type == "single_event"%}
- Single event on {{ single_event_date }}
+ {% if form.event_type == "single_event"%}
+ Single event on {{ form.single_event_date }}
{% endif %}
- {% if event_type == "recurrent_event"%}
- Recurrent event for the month of {{ recurrent_event_month }}
+ {% if form.event_type == "recurrent_event"%}
+ Recurrent event for the month of {{ form.recurrent_event_month }}
{% endif %}
Break data:
+ Start: {{ form.lunch_start | minutes_to_time }}    End: {{ form.lunch_finish | minutes_to_time }}
{% else%}
No
{% endif %}
- Coffee breaks: {{ coffee_breaks }}
- {% if coffee_breaks > 0 %}
- each of {{ coffee_duration }} minutes duration
+
Coffee breaks: {{ form.coffee_breaks }}
+ {% if form.coffee_breaks > 0 %}
+ each of {{ form.coffee_duration }} minutes duration
- {%- for coffee in coffee_times %}
- Coffee break {{ loop.index }}: Start: {{ coffee[0] }}    End: {{ coffee[1] }}
+ {%- for start_time, end_time in form.coffee_break_times() %}
+ Coffee break {{ loop.index }}: Start: {{ start_time | minutes_to_time }}    End: {{ end_time | minutes_to_time }}
{%- endfor %}
{% endif %}
@@ -111,7 +111,7 @@
Mask wearing:
From 641d236aa93e77e524c62547192e3cbbd0ba8757 Mon Sep 17 00:00:00 2001
From: Phil Elson
Date: Fri, 6 Nov 2020 23:41:42 +0100
Subject: [PATCH 2/2] Prevent arbitrary code execution exploits.
---
cara/apps/calculator/model_generator.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py
index 907a78b2..3ad6bc37 100644
--- a/cara/apps/calculator/model_generator.py
+++ b/cara/apps/calculator/model_generator.py
@@ -1,5 +1,6 @@
from cara.models import Model
from dataclasses import dataclass
+import html
import typing
from cara import models
@@ -67,6 +68,11 @@ class FormData:
if form_data[key] not in valid_set:
raise ValueError(f"{form_data[key]} is not a valid value for {key}")
+ # Don't let arbirtrary unescaped HTML through the net.
+ for key, value in form_data.items():
+ if isinstance(value, str):
+ form_data[key] = html.escape(value)
+
# TODO: This fixup is a problem with the form.html.
for key, value in form_data.items():
if value == "":