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>
This commit is contained in:
31
backend/app/services/sanitize.py
Normal file
31
backend/app/services/sanitize.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user