Files
pesco-ncr/backend/app/services/sanitize.py
ang3l12 dea316b113 Initial commit: PESCO NCR system
Complete Non-Conformance Report system replacing the PowerApps/SharePoint
prototype: FastAPI + SQLAlchemy 2 (async) + Alembic + MySQL 8 backend,
React 18 + Vite + TypeScript + MUI frontend, Entra ID auth (MSAL / JWKS,
group-gated), Microsoft Graph delegated Mail.Send notifications (OBO),
six-stage workflow state machine with server-side enforcement, atomic
NCR-YYYY-NNNN numbering, attachments with camera capture, immutable
field-level audit trail, admin reopen, reports + CSV export, WeasyPrint
PDF traveler, Power BI reporting views + read-only DB user, documented
VISUAL ERP job-lookup stub, pytest suite (26 tests), docker-compose
deployment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 11:41:22 -06:00

32 lines
844 B
Python

"""Rich-text HTML sanitization (XSS defense) using nh3 (ammonia bindings).
Applied server-side to every rich-text field before it is stored."""
import nh3
_ALLOWED_TAGS = {
"p", "br", "div", "span",
"strong", "b", "em", "i", "u", "s", "sub", "sup",
"ul", "ol", "li",
"h1", "h2", "h3", "h4",
"blockquote", "pre", "code",
"a", "hr", "table", "thead", "tbody", "tr", "th", "td",
}
_ALLOWED_ATTRIBUTES = {
"a": {"href", "title"},
"th": {"colspan", "rowspan"},
"td": {"colspan", "rowspan"},
}
def sanitize_html(value: str | None) -> str | None:
if value is None:
return None
cleaned = nh3.clean(
value,
tags=_ALLOWED_TAGS,
attributes=_ALLOWED_ATTRIBUTES,
link_rel="noopener noreferrer",
url_schemes={"http", "https", "mailto"},
)
return cleaned