fix(stability): fail CLOSED on feed-sanitize exception + cover bulk-SQL/flush (review round 1)

#1 [security] The final feed-sanitize try/except was FAIL-OPEN: since 4.4 removed
the per-fragment passes, post['html'] / the concatenated feed html is now raw
channel-controlled HTML, and this is its ONLY sanitize. If bleach itself throws
(RecursionError/OOM on pathological nested HTML — a class already seen in this
project), the except branch returned the RAW payload = stored XSS. Both branches
(RSS per-post, HTML whole-feed) now fail CLOSED via html.escape, so the content
survives as inert text and no live tag ever reaches the client. Added a test that
forces bleach to raise and asserts no live <script>/<img>/<a> reaches the feed
(adversarially validated: reverting to fail-open makes it fail).

#2 [test] Direct test of the new upsert_media_file_ids_bulk_sync on a temp DB:
empty no-op, multi-row insert, and re-upsert-updates-added (the real executemany +
ON CONFLICT, previously only mocked).

#3 [test] Test that media ids collected before a render exception are still
flushed (the flush is in a finally) — removing the finally now turns a test red.

#4 [cleanup] Removed the dead  in rss_generator (the
flush is delegated to post_parser._flush_pending_media_ids).

#5 [doc] Corrected the sanitize-map comments: RSS sanitizes per-post, only HTML is
the whole-feed pass.

233 passed (214 baseline + 19).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude code agent
2026-07-05 09:41:09 +03:00
parent 9f9091f6fb
commit 8d21390294
3 changed files with 120 additions and 6 deletions
+5 -4
View File
@@ -526,7 +526,8 @@ class PostParser:
sanitize: when True, run the html body and footer through a single
bleach pass each. Single-post HTML and JSON need this (there is no
whole-feed pass on those paths). Feed generation passes False and
relies on the final whole-feed sanitize in rss_generator, so no
relies on the final sanitize in rss_generator (per-post for RSS,
whole-feed for HTML), so no
fragment is sanitized more than once.
"""
# Compute html body once — avoids triple _generate_html_body calls.
@@ -715,8 +716,8 @@ class PostParser:
content_body.append(f'</div><br>')
# NOTE: sanitize is NOT applied here. Sanitization happens exactly once per
# output boundary (process_message for single-post/JSON, final whole-feed pass
# in rss_generator for feeds). See the sanitize coverage map (4.4).
# output boundary (process_message for single-post/JSON; in rss_generator the
# per-post pass for RSS and the whole-feed pass for HTML). See the map (4.4).
html_body = '\n'.join(content_body)
return html_body
@@ -879,7 +880,7 @@ class PostParser:
content_footer.append('<br>' + flags_html)
# Not sanitized here — sanitized once at the output boundary (4.4 coverage map):
# process_message for single-post/JSON, final whole-feed pass for feeds.
# process_message for single-post/JSON; per-post (RSS) / whole-feed (HTML) for feeds.
html_footer = '\n'.join(content_footer)
return html_footer