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>
32 lines
844 B
Python
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
|