moved cleaning to create where possible. Changed nh_safe to safe as we assume everything was cleaned in the first place

This commit is contained in:
Liam Brydon
2025-11-06 09:46:30 +13:00
parent c663eaf7bd
commit 4524b8fdf3
49 changed files with 232 additions and 263 deletions

View File

@ -11,6 +11,7 @@ from typing import Union, List, Optional, Dict, Tuple, Set, Any
from uuid import uuid4
import math
import joblib
import nh3
import numpy as np
from django.conf import settings as s
from django.contrib.auth.models import AbstractUser
@ -790,12 +791,11 @@ class Compound(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdentifierMixin
if name is None or name.strip() == "":
name = f"Compound {Compound.objects.filter(package=package).count() + 1}"
c.name = name
# Clean for potential XSS
c.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
# We have a default here only set the value if it carries some payload
if description is not None and description.strip() != "":
c.description = description.strip()
c.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
c.save()
@ -967,11 +967,11 @@ class CompoundStructure(EnviPathModel, AliasMixin, ScenarioMixin, ChemicalIdenti
raise ValueError("Unpersisted Compound! Persist compound first!")
cs = CompoundStructure()
# Clean for potential XSS
if name is not None:
cs.name = name
cs.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None:
cs.description = description
cs.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
cs.smiles = smiles
cs.compound = compound
@ -1144,18 +1144,18 @@ class SimpleAmbitRule(SimpleRule):
if name is None or name.strip() == "":
name = f"Rule {Rule.objects.filter(package=package).count() + 1}"
r.name = name
# Clean for potential XSS
r.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None and description.strip() != "":
r.description = description
r.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
r.smirks = smirks
r.smirks = nh3.clean(smirks).strip()
if reactant_filter_smarts is not None and reactant_filter_smarts.strip() != "":
r.reactant_filter_smarts = reactant_filter_smarts
r.reactant_filter_smarts = nh3.clean(reactant_filter_smarts).strip()
if product_filter_smarts is not None and product_filter_smarts.strip() != "":
r.product_filter_smarts = product_filter_smarts
r.product_filter_smarts = nh3.clean(product_filter_smarts).strip()
r.save()
return r
@ -1356,12 +1356,11 @@ class Reaction(EnviPathModel, AliasMixin, ScenarioMixin, ReactionIdentifierMixin
r = Reaction()
r.package = package
# Clean for potential XSS
if name is not None and name.strip() != "":
r.name = name
r.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None and name.strip() != "":
r.description = description
r.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
r.multi_step = multi_step
@ -1663,10 +1662,10 @@ class Pathway(EnviPathModel, AliasMixin, ScenarioMixin):
if name is None or name.strip() == "":
name = f"Pathway {Pathway.objects.filter(package=package).count() + 1}"
pw.name = name
# Clean for potential XSS
pw.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None and description.strip() != "":
pw.description = description
pw.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
pw.save()
try:
@ -1961,11 +1960,15 @@ class Edge(EnviPathModel, AliasMixin, ScenarioMixin):
for node in end_nodes:
e.end_nodes.add(node)
# Clean for potential XSS
# Cleaning technically not needed as it is also done in Reaction.create, including it here for consistency
if name is None:
name = f"Reaction {pathway.package.reactions.count() + 1}"
name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is None:
description = s.DEFAULT_VALUES["description"]
description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
r = Reaction.create(
pathway.package,
@ -2482,10 +2485,10 @@ class RuleBasedRelativeReasoning(PackageBasedModel):
if name is None or name.strip() == "":
name = f"RuleBasedRelativeReasoning {RuleBasedRelativeReasoning.objects.filter(package=package).count() + 1}"
rbrr.name = name
# Clean for potential XSS
rbrr.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None and description.strip() != "":
rbrr.description = description
rbrr.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
if threshold is None or (threshold <= 0 or 1 <= threshold):
raise ValueError("Threshold must be a float between 0 and 1.")
@ -2591,10 +2594,10 @@ class MLRelativeReasoning(PackageBasedModel):
if name is None or name.strip() == "":
name = f"MLRelativeReasoning {MLRelativeReasoning.objects.filter(package=package).count() + 1}"
mlrr.name = name
# Clean for potential XSS
mlrr.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None and description.strip() != "":
mlrr.description = description
mlrr.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
if threshold is None or (threshold <= 0 or 1 <= threshold):
raise ValueError("Threshold must be a float between 0 and 1.")
@ -2954,10 +2957,10 @@ class EnviFormer(PackageBasedModel):
if name is None or name.strip() == "":
name = f"EnviFormer {EnviFormer.objects.filter(package=package).count() + 1}"
mod.name = name
# Clean for potential XSS
mod.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None and description.strip() != "":
mod.description = description
mod.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
if threshold is None or (threshold <= 0 or 1 <= threshold):
raise ValueError("Threshold must be a float between 0 and 1.")
@ -3400,41 +3403,43 @@ class Scenario(EnviPathModel):
scenario_type: str,
additional_information: List["EnviPyModel"],
):
s = Scenario()
s.package = package
new_s = Scenario()
new_s.package = package
if name is None or name.strip() == "":
name = f"Scenario {Scenario.objects.filter(package=package).count() + 1}"
s.name = name
new_s.name = nh3.clean(name, tags=s.ALLOWED_HTML_TAGS).strip()
if description is not None and description.strip() != "":
s.description = description
new_s.description = nh3.clean(description, tags=s.ALLOWED_HTML_TAGS).strip()
if scenario_date is not None and scenario_date.strip() != "":
s.scenario_date = scenario_date
new_s.scenario_date = nh3.clean(scenario_date).strip()
if scenario_type is not None and scenario_type.strip() != "":
s.scenario_type = scenario_type
new_s.scenario_type = scenario_type
add_inf = defaultdict(list)
for info in additional_information:
cls_name = info.__class__.__name__
ai_data = json.loads(info.model_dump_json())
# Clean for potential XSS hidden in the additional information fields.
ai_data = json.loads(nh3.clean(info.model_dump_json()).strip())
ai_data["uuid"] = f"{uuid4()}"
add_inf[cls_name].append(ai_data)
s.additional_information = add_inf
new_s.additional_information = add_inf
s.save()
new_s.save()
return s
return new_s
@transaction.atomic
def add_additional_information(self, data: "EnviPyModel"):
cls_name = data.__class__.__name__
ai_data = json.loads(data.model_dump_json())
# Clean for potential XSS hidden in the additional information fields.
ai_data = json.loads(nh3.clean(data.model_dump_json()).strip())
ai_data["uuid"] = f"{uuid4()}"
if cls_name not in self.additional_information:
@ -3469,7 +3474,8 @@ class Scenario(EnviPathModel):
new_ais = defaultdict(list)
for k, vals in data.items():
for v in vals:
ai_data = json.loads(v.model_dump_json())
# Clean for potential XSS hidden in the additional information fields.
ai_data = json.loads(nh3.clean(v.model_dump_json()).strip())
if hasattr(v, "uuid"):
ai_data["uuid"] = str(v.uuid)
else: