[Fix] Mitigate XSS attack vector by cleaning input before it hits our Database (#171)

## Changes

- All text input fields are now cleaned with nh3 to remove html tags. We allow certain html tags under `settings.py/ALLOWED_HTML_TAGS` so we can easily update the tags we allow in the future.
- All names and descriptions now use the template tag `nh_safe` in all html files.
- Usernames and emails are a small exception and are not allowed any html tags

Co-authored-by: Liam Brydon <62733830+MyCreativityOutlet@users.noreply.github.com>
Co-authored-by: jebus <lorsbach@envipath.com>
Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#171
Reviewed-by: jebus <lorsbach@envipath.com>
Co-authored-by: liambrydon <lbry121@aucklanduni.ac.nz>
Co-committed-by: liambrydon <lbry121@aucklanduni.ac.nz>
This commit is contained in:
2025-11-11 22:49:55 +13:00
committed by jebus
parent 1cccefa991
commit 34589efbde
53 changed files with 444 additions and 230 deletions

View File

@ -13,7 +13,7 @@
<div class="panel-group" id="rule-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ rule.name }}
{{ rule.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -60,7 +60,7 @@
<div id="rule-reaction-patterns" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for r in rule.srs %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
<div align="center">
<p>
{{r.as_svg|safe}}
@ -81,7 +81,7 @@
<div id="rule-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in rule.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>

View File

@ -15,7 +15,7 @@
<div class="panel-group" id="compound-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ compound.name }}
{{ compound.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -64,7 +64,7 @@
</div>
<div id="compound-desc" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{{ compound.description }}
{{ compound.description|safe }}
</div>
</div>
@ -133,7 +133,7 @@
<div id="compound-reaction" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for r in compound.related_reactions %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }} <i>({{ r.package.name }})</i></a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }} <i>({{ r.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>
@ -150,7 +150,7 @@
<div id="compound-pathway" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for r in compound.related_pathways %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }} <i>({{ r.package.name }})</i></a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }} <i>({{ r.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>
@ -167,7 +167,7 @@
<div id="compound-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in compound.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>

View File

@ -13,7 +13,7 @@
<div class="panel-group" id="compound-structure-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ compound_structure.name }}
{{ compound_structure.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -28,7 +28,7 @@
</div>
</div>
<div class="panel-body">
<p> {{ compound_structure.description }} </p>
<p> {{ compound_structure.description|safe }} </p>
</div>
<!-- Image -->
@ -86,7 +86,7 @@
<div id="compound-structure-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in compound_structure.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>

View File

@ -12,7 +12,7 @@
<div class="panel-group" id="edge-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ edge.edge_label.name }}
{{ edge.edge_label.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -36,7 +36,7 @@
</div>
<div id="edge-desc" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{{ edge.description }}
{{ edge.description|safe }}
</div>
</div>
@ -82,12 +82,12 @@
<div id="edge-description-smiles" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for educt in edge.start_nodes.all %}
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name }}</a>
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name|safe }}</a>
{% endfor %}
<span class="glyphicon glyphicon-arrow-right" style="margin-left:5em;margin-right:5em;"
aria-hidden="true"></span>
{% for product in edge.end_nodes.all %}
<a class="btn btn-default" href="{{ product.url }}">{{ product.name }}</a>
<a class="btn btn-default" href="{{ product.url }}">{{ product.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -116,7 +116,7 @@
<div id="edge-rules" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for r in edge.edge_label.rules.all %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -132,7 +132,7 @@
<div id="edge-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in edge.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>

View File

@ -11,7 +11,7 @@
<div class="panel-group" id="package-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ group.name }}
{{ group.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -26,7 +26,7 @@
</div>
</div>
<div class="panel-body">
<p> {{ group.description }} </p>
<p> {{ group.description|safe }} </p>
</div>
</div>
@ -39,10 +39,10 @@
</div>
<ul class="list-group">
{% for um in group.user_member.all %}
<a class="list-group-item" href="{{ um.url }}">{{ um.username }}</a>
<a class="list-group-item" href="{{ um.url }}">{{ um.username|safe }}</a>
{% endfor %}
{% for gm in group.group_member.all %}
<a class="list-group-item" href="{{ gm.url }}">{{ gm.name }}</a>
<a class="list-group-item" href="{{ gm.url }}">{{ gm.name|safe }}</a>
{% endfor %}
</ul>
</div>
@ -56,7 +56,7 @@
</div>
<ul class="list-group">
{% for p in packages %}
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
{% endfor %}
</ul>
</div>

View File

@ -1,6 +1,5 @@
{% extends "framework.html" %}
{% load static %}
{% load envipytags %}
{% block content %}
{% block action_modals %}
@ -18,7 +17,7 @@
<div class="panel-group" id="model-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ model.name }}
{{ model.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -33,7 +32,7 @@
</div>
</div>
<div class="panel-body">
<p> {{ model.description }} </p>
<p> {{ model.description|safe }} </p>
</div>
{% if model|classname == 'MLRelativeReasoning' or model|classname == 'RuleBasedRelativeReasoning'%}
<!-- Rule Packages -->
@ -46,7 +45,7 @@
<div id="rule-package" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for p in model.rule_packages.all %}
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -60,7 +59,7 @@
<div id="reaction-package" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for p in model.data_packages.all %}
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -75,7 +74,7 @@
<div id="eval-package" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for p in model.eval_packages.all %}
<a class="list-group-item" href="{{ p.url }}">{{ p.name }}</a>
<a class="list-group-item" href="{{ p.url }}">{{ p.name|safe }}</a>
{% endfor %}
</div>
</div>

View File

@ -12,7 +12,7 @@
<div class="panel-group" id="node-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ node.name }}
{{ node.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -39,7 +39,7 @@
</div>
<div id="node-desc" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{{ node.description }}
{{ node.description|safe }}
</div>
</div>
@ -98,7 +98,7 @@
<div id="node-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in node.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>

View File

@ -14,7 +14,7 @@
<div class="panel-group" id="package-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ package.name }}
{{ package.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"

View File

@ -98,7 +98,7 @@
<div class="panel-group" id="pwAccordion">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ pathway.name }}
{{ pathway.name|safe }}
</div>
</div>
<div class="panel panel-default panel-heading list-group-item" style="background-color:silver">
@ -236,7 +236,7 @@
<div id="pathway-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in pathway.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>
@ -266,7 +266,7 @@
<td colspan="2">
<li class="list-group-item">
<a href="{{ pathway.setting.model.url }}">
{{ pathway.setting.model.name }}
{{ pathway.setting.model.name|safe }}
</a>
</li>
</td>
@ -299,7 +299,7 @@
{% for p in pathway.setting.rule_packages.all %}
<li class="list-group-item">
<a href="{{ p.url }}">
{{ p.name }}
{{ p.name|safe }}
</a>
</li>
{% endfor %}

View File

@ -14,7 +14,7 @@
<div class="panel-group" id="reaction-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ reaction.name }}
{{ reaction.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -38,7 +38,7 @@
</div>
<div id="reaction-desc" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{{ reaction.description }}
{{ reaction.description|safe }}
</div>
</div>
@ -84,12 +84,12 @@
<div id="reaction-description-smiles" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for educt in reaction.educts.all %}
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name }}</a>
<a class="btn btn-default" href="{{ educt.url }}">{{ educt.name|safe }}</a>
{% endfor %}
<span class="glyphicon glyphicon-arrow-right" style="margin-left:5em;margin-right:5em;"
aria-hidden="true"></span>
{% for product in reaction.products.all %}
<a class="btn btn-default" href="{{ product.url }}">{{ product.name }}</a>
<a class="btn btn-default" href="{{ product.url }}">{{ product.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -118,7 +118,7 @@
<div id="reaction-rules" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for r in reaction.rules.all %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -152,7 +152,7 @@
<div id="reaction-pathway" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for r in reaction.related_pathways %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -168,7 +168,7 @@
<div id="reaction-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in reaction.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }} <i>({{ s.package.name }})</i></a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }} <i>({{ s.package.name|safe }})</i></a>
{% endfor %}
</div>
</div>

View File

@ -10,7 +10,7 @@
<div class="panel-group" id="scenario-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ scenario.name }}
{{ scenario.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -30,7 +30,7 @@
<div class="panel panel-default">
<div class="panel-heading">Description</div>
<div class="panel-body">
{{ scenario.description }}
{{ scenario.description|safe }}
<br>
{{ scenario.scenario_type }}
<br>

View File

@ -13,7 +13,7 @@
<div class="panel-group" id="rule-detail">
<div class="panel panel-default">
<div class="panel-heading" id="headingPanel" style="font-size:2rem;height: 46px">
{{ rule.name }}
{{ rule.name|safe }}
<div id="actionsButton"
style="float: right;font-weight: normal;font-size: medium;position: relative; top: 50%; transform: translateY(-50%);z-index:100;display: none;"
class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
@ -29,7 +29,7 @@
</div>
<div class="panel-body">
<p>
{{ rule.description }}
{{ rule.description|safe }}
</p>
</div>
@ -145,7 +145,7 @@
<div id="rule-composite-rule" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for cr in rule.parallelrule_set.all %}
<a class="list-group-item" href="{{ cr.url }}">{{ cr.name }}</a>
<a class="list-group-item" href="{{ cr.url }}">{{ cr.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -162,7 +162,7 @@
<div id="rule-scenario" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
{% for s in rule.scenarios.all %}
<a class="list-group-item" href="{{ s.url }}">{{ s.name }}</a>
<a class="list-group-item" href="{{ s.url }}">{{ s.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -179,7 +179,7 @@
<div id="rule-reaction" class="panel-collapse collapse">
<div class="panel-body list-group-item">
{% for r in rule.related_reactions %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
{% endfor %}
</div>
</div>
@ -196,7 +196,7 @@
<div id="rule-pathway" class="panel-collapse collapse">
<div class="panel-body list-group-item">
{% for r in rule.related_pathways %}
<a class="list-group-item" href="{{ r.url }}">{{ r.name }}</a>
<a class="list-group-item" href="{{ r.url }}">{{ r.name|safe }}</a>
{% endfor %}
</div>
</div>

View File

@ -41,7 +41,7 @@
<div id="default-package" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
<li class="list-group-item">
<a href="{{ user.default_package.url }}"> {{ user.default_package.name }}</a>
<a href="{{ user.default_package.url }}"> {{ user.default_package.name|safe }}</a>
</li>
</div>
</div>
@ -58,7 +58,7 @@
<div class="panel-body list-group-item">
{% for g in meta.available_groups %}
<li class="list-group-item">
<a href="{{ g.url }}"> {{ g.name }}</a>
<a href="{{ g.url }}"> {{ g.name|safe }}</a>
</li>
{% endfor %}
</div>
@ -90,7 +90,7 @@
<td colspan="2">
<li class="list-group-item">
<a href="{{user.default_setting.model.url}}">
{{ user.default_setting.model.name }}
{{ user.default_setting.model.name|safe }}
</a>
</li>
</td>
@ -123,7 +123,7 @@
{% for p in user.default_setting.rule_packages.all %}
<li class="list-group-item">
<a href="{{p.url}}">
{{ p.name }}
{{ p.name|safe }}
</a>
</li>
{% endfor %}