Replace custom HTML stripper and sanitize admin UI post rendering #44
Loading…
Reference in a new issue
No description provided.
Delete branch "refs/pull/44/head"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Resolves M4 and M5 from the security audit by introducing nh3 (Rust ammonia binding) for all Fediverse post HTML handling.
M5 — Custom HTML stripper replaced
The hand-rolled
_HTMLStripperclass inapi/curated.pyis replaced withnh3.clean(content, tags=set())+html.unescape(). nh3 uses a spec-compliant HTML5 parser and correctly handles SVG namespaces and<style>tag content thathtml.parserpasses through. Two new tests added.M4 — Admin UI post rendering sanitized
{{ post.content|safe }}removed from both admin templates. Asanitize_htmlJinja2 filter is registered intemplates_env.py; it callsnh3.clean()with an allowlist of safe formatting tags (a, br, em, p, span, strong) and returnsmarkupsafe.Markupso Jinja2 does not double-escape the output. Six tests added.620 tests passing (was 572).
Closes #43
packages/fenliu/tests/test_strip_html.pyline 26 — @marvin8✅ False positive — the reviewer evaluated the old
_HTMLStripperbehaviour. Withnh3.clean(text, tags=set()), SVG and all nested tags including<script>are fully stripped; the text nodealert(1)inside<script>is also removed because nh3/ammonia treats script content as raw text and discards it. The test passes.packages/fenliu/tests/test_strip_html.pyline 30 — @marvin8✅ False positive — again evaluated against the old code.
nh3.clean()strips<style>tag content entirely; the resulting plain text is justhello. This is one of the cases that motivated the switch from_HTMLStripperto nh3. The test passes.packages/fenliu/tests/test_sanitize_html.pyline 53 — @marvin8✅ Valid point — nh3/ammonia strips
javascript:hrefs by default (thehrefattribute is removed entirely, leaving a safe<a rel="noopener noreferrer">tag). Added a test in commite869079to document and lock in this behaviour:packages/fenliu/src/fenliu/api/curated.pyline 50 — @marvin8🔴 Declining.
nh3.clean()accepts any Pythonstrand does not raise on malformed HTML — that is its design contract (it sanitises rather than validates).html.unescape()is a pure stdlib function with no error path for valid strings. Adding atry/excepthere would swallow real bugs (e.g. a type error from a non-string caller) and provide no safety benefit for the actual inputs these functions receive.packages/fenliu/src/fenliu/templates_env.pyline 17 — @marvin8🔴 Declining the error-handling part for the same reason as above —
nh3.clean()does not raise on string input.On the readability concern: the
# noqa: S704suppression comment is intentional — S704 flags unsafeMarkup()wrapping, but ruff cannot know thatnh3.clean()output is already sanitised. The suppression comment itself serves as a signal to future maintainers that this is a deliberate security decision, not an oversight.packages/fenliu/README.mdline 15 — @marvin8✅ Correct catch. A
replace_allincorrectly updated the historical "What's New" total alongside the live counts. Fixed in commite869079: theWhat's New in v0.7.1line is restored to572 total(accurate at release time).For context: the current total of 620 is correct — the main branch already had 612 tests before this PR (the README's 572 was already stale from earlier PRs that added tests without updating it), and this PR adds 8 more.
packages/fenliu/README.mdline 173 — @marvin8✅ This is the exact line fixed in commit
e869079— restored to572 total.packages/fenliu/Security-Audit-detail.mdline 401 — @marvin8✅ No action needed —
nh3~=0.3.5is present inpyproject.toml(committed in the first commit of this PR alongside theuv.lockandpylock.tomlupdates). The reviewer hit a diff-context limitation, not a missing change.packages/fenliu/Security-Audit-detail.mdline 436 — @marvin8✅ Acknowledged. No action needed.
packages/fenliu/Security-Audit.mdline 22 — @marvin8🔴 False positive. The format uses
*as the Markdown italic delimiter:*(resolved: ...)*. The parentheses inside are balanced — one(beforeresolvedand one)before the closing*. This is identical to every other resolved entry in the file (C1, H1–H4). No change needed.