coulomb-social/tests/test_rendering_audit.py

109 lines
5 KiB
Python
Raw Normal View History

"""Rendering review uses synthetic notes and never accepts private corpus fidelity."""
import hashlib
import json
import os
import subprocess
import sys
from pathlib import Path
from coulomb_social.apps.spaces.content import render_markdown
from coulomb_social.apps.spaces.rendering_audit import audit_body, review_card
def test_audit_uses_product_renderer_and_reports_custom_tags_attributes_and_schemes():
body = ('# Synthetic\n\n<topic>Placeholder</topic><br>Text\n'
'<img src="assets/picture.png" onerror="synthetic()">'
'<a href="javascript:synthetic()">Unsafe</a>')
audit, rendered = audit_body(body)
assert rendered == render_markdown(body)
assert audit["sanitization_changed_html"]
assert audit["markup_changed_by_sanitization"]
assert not audit["serialization_only_change"]
assert audit["removed_tag_counts"] == {"topic": 1}
assert {a["attribute"] for a in audit["removed_attributes"]} == {"onerror", "href"}
assert "assets/picture.png" in rendered and "<br>" in rendered
assert audit["body_sha256"] == hashlib.sha256(body.encode()).hexdigest()
assert not audit["original_bubble_render_compared"]
assert not audit["productive_fidelity_accepted"]
assert audit["reference_occurrences_before"] == 2
assert audit["reference_occurrences_after"] == 1
assert not audit["reference_attributes_preserved"]
assert audit["removed_reference_occurrences"] == 1
def test_link_entities_duplicates_and_relative_assets_preserve_exact_reference_attributes():
body = ('[first](https://example.invalid/?a=1&b=2#part)\n\n'
'[second](https://example.invalid/?a=1&b=2#part)\n\n'
'<a href="https://example.invalid/?a=1&amp;b=2#part">third</a>'
'<img src="assets/synthetic.png">')
audit, _ = audit_body(body)
assert audit['reference_occurrences_before'] == 4
assert audit['reference_occurrences_after'] == 4
assert audit['reference_attributes_preserved']
assert audit['removed_reference_occurrences'] == 0
assert audit['added_reference_occurrences'] == 0
def test_serialized_line_break_difference_is_not_reported_as_lost_markup():
audit, _ = audit_body("First<br/>Second")
assert audit["serialization_only_change"]
assert not audit["markup_changed_by_sanitization"]
assert not audit["visible_text_changed_by_sanitization"]
def test_continued_numbered_lists_keep_their_starts_through_sanitization():
body = "2. Second item\n3. Third item\n\nSeparate paragraph.\n\n8. Eighth item"
audit, rendered = audit_body(body)
assert '<ol start="2">' in rendered
assert '<ol start="8">' in rendered
assert not audit["markup_changed_by_sanitization"]
assert not audit["visible_text_changed_by_sanitization"]
assert not audit["productive_fidelity_accepted"]
def test_list_start_permission_does_not_allow_event_handlers_or_custom_markup():
body = ('<ol start="4" onclick="synthetic()"><li>Fourth item</li></ol>'
'<topic name="synthetic">Placeholder</topic>'
'<a href="javascript:synthetic()">Link</a>')
audit, rendered = audit_body(body)
assert '<ol start="4">' in rendered
assert 'onclick' not in rendered and 'javascript:' not in rendered
assert '<topic' not in rendered and 'Placeholder' in rendered
assert {a["attribute"] for a in audit["removed_attributes"]} == {
"onclick", "name", "href",
}
def test_review_card_escapes_source_and_blocks_resources_and_interaction():
body = '<img src="https://example.invalid/synthetic.png"><script>synthetic()</script>'
audit, rendered = audit_body(body)
card = review_card("</h1><script>synthetic()</script>", body, rendered, audit,
source_id='a/"?b').decode()
assert "<script>" not in card
assert "&lt;script&gt;" in card
assert "default-src 'none'" in card
assert "base-uri 'none'" in card and "form-action 'none'" in card
assert "<div inert>" in card
assert 'https://coulomb.social/pg_chunk/a%2F%22%3Fb' in card
def test_cli_keeps_missing_bodies_and_private_permissions_and_refuses_overwrite(tmp_path):
repo = Path(__file__).resolve().parents[1]
source = tmp_path / "articles.json"
source.write_text(json.dumps({"a": {"content_text": "Synthetic", "nomer_text": "Private"},
"b": {"_id": "b"}}))
output = tmp_path / "audit"
cmd = [sys.executable, str(repo / "scripts/audit-bubble-rendering.py"), str(source),
"--output", str(output)]
result = subprocess.run(cmd, capture_output=True, text=True)
assert result.returncode == 0, result.stderr
assert "Private" not in result.stdout
report = json.loads((output / "audit.json").read_text())
assert len(report["records"]) == 2
assert report["records"][1]["status"] == "body_missing"
assert (output / "index.html").is_file()
assert os.stat(output).st_mode & 0o777 == 0o700
assert all(os.stat(p).st_mode & 0o777 == 0o600 for p in output.iterdir())
assert subprocess.run(cmd, capture_output=True).returncode == 1