mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 00:27:20 -05:00
fix: render pack probe output as text
Run output on a platform's Tested block is escaped for HTML, MDX and Markdown. inline_html links only http(s) and relative targets, quotes escaped. Pack probe runs stay off the emulator pages, shown on the platform's.
This commit is contained in:
1 parent
a2d30046f2
commit
905e3853df
5 files changed
+75
-11
No files matched your search
@@ -89,6 +89,8 @@ def records_path(emulator: str) -> str:
|
||||
FINDINGS = "findings"
|
||||
CAMPAIGN_RUNS = "campaign-runs"
|
||||
PACK_PROBES = "pack-probes"
|
||||
# The subject of every core_probe record a pack probe writes.
|
||||
PACK_SUBJECT_PREFIX = "pack-"
|
||||
CAMPAIGN_FILES = {
|
||||
FINDINGS: ("found_at", "campaign-finding.schema.json"),
|
||||
CAMPAIGN_RUNS: ("started_at", "campaign-run.schema.json"),
|
||||
|
||||
@@ -514,7 +514,8 @@ def probe_argv(core: dict, choice: CoreChoice, content: campaign.Content, load:
|
||||
|
||||
|
||||
def subject_for(platform: str, install: str, system: str, so: str, content: str) -> str:
|
||||
return mr.slugify(f"pack {platform} {install} {system} {so} {content}", 90)
|
||||
subject = mr.slugify(f"{platform} {install} {system} {so} {content}", 85)
|
||||
return f"{mr.PACK_SUBJECT_PREFIX}{subject}"
|
||||
|
||||
|
||||
def probe_one(layout: Layout, platform: str, config: dict, profile: dict, choice: CoreChoice,
|
||||
|
||||
+15
-1
@@ -75,11 +75,25 @@ def inline_html(text: str) -> str:
|
||||
continue
|
||||
part = html.escape(part, quote=False)
|
||||
part = re.sub(r"\*\*([^*]+)\*\*", r"<strong>\1</strong>", part)
|
||||
part = re.sub(r"\[([^\]]+)\]\(([^)\s]+)\)", r'<a href="\2">\1</a>', part)
|
||||
part = re.sub(r"\[([^\]]+)\]\(([^)\s]+)\)", _link, part)
|
||||
out.append(part)
|
||||
return "".join(out)
|
||||
|
||||
|
||||
# A scheme before any slash: javascript:, data:, vbscript: and the like.
|
||||
_SCHEME = re.compile(r"^[A-Za-z][A-Za-z0-9+.-]*:")
|
||||
|
||||
|
||||
def _link(match: re.Match) -> str:
|
||||
"""A Markdown link as an anchor when its target is http(s) or relative,
|
||||
else left as the text it was. The target is already escaped for text,
|
||||
quotes are escaped for the attribute."""
|
||||
label, href = match.group(1), match.group(2)
|
||||
if _SCHEME.match(href) and not re.match(r"^https?://", href, re.IGNORECASE):
|
||||
return match.group(0)
|
||||
return f'<a href="{href.replace(chr(34), """).replace(chr(39), "'")}">{label}</a>'
|
||||
|
||||
|
||||
def _indented(lines: list[str], pad: str) -> list[str]:
|
||||
return [f"{pad}{line}" if line.strip() else "" for line in lines]
|
||||
|
||||
|
||||
@@ -280,12 +280,22 @@ def packs_section(items: list[dict]) -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
def _inert(value: object) -> str:
|
||||
"""Run output (a core's log line, a file name it opened) placed in raw
|
||||
HTML as text: every character HTML, MDX or Markdown reads as markup,
|
||||
quotes included, written as a character reference."""
|
||||
return _plain(value).replace('"', """).replace("'", "'")
|
||||
|
||||
|
||||
def _pack_details(item: dict) -> list[str]:
|
||||
def line(label: str, value: object) -> str:
|
||||
return _sentence_html(label, _inert(value), markup_ready=True)
|
||||
|
||||
lines = [
|
||||
_sentence_html("Result", item["detail"]),
|
||||
_sentence_html("Core", f"{item['core']} ({item['emulator']})"),
|
||||
_sentence_html("Files the core opened", "; ".join(item["files"]) or "none"),
|
||||
_sentence_html("Findings", "; ".join(item["finding_list"]) or "none"),
|
||||
line("Result", item["detail"]),
|
||||
line("Core", f"{item['core']} ({item['emulator']})"),
|
||||
line("Files the core opened", "; ".join(item["files"]) or "none"),
|
||||
line("Findings", "; ".join(item["finding_list"]) or "none"),
|
||||
f'<p><a href="{_attr(item["record_url"])}">Full record</a>.</p>' if item["record_url"] else "",
|
||||
]
|
||||
return [line for line in lines if line]
|
||||
@@ -308,8 +318,8 @@ def pack_tested_section(items: list[dict]) -> list[str]:
|
||||
'<div class="rb-tested-head" aria-hidden="true"><span>Test</span>'
|
||||
"<span>Result</span><span>Installed by</span><span>Date</span></div>"]
|
||||
for item in sorted(items, key=lambda i: (i["system"], i["content"], i["core"], i["install"])):
|
||||
title = (f"{_cell(item['system'])}, {_cell(short_content(item['content']))} "
|
||||
f"<small>{html.escape(item['core'])}</small>")
|
||||
title = (f"{_inert(item['system'])}, {_inert(short_content(item['content']))} "
|
||||
f"<small>{_inert(item['core'])}</small>")
|
||||
lines += [
|
||||
'<details class="rb-tested-row">',
|
||||
("<summary>"
|
||||
@@ -646,6 +656,9 @@ def by_emulator(index: dict) -> dict[str, list[dict]]:
|
||||
opened[line["emulator"]] = opened.get(line["emulator"], 0) + 1
|
||||
out: dict[str, list[dict]] = {}
|
||||
for item in index.get("items", []):
|
||||
# A pack probe's run is shown on its platform's page, beside the pack.
|
||||
if item["subject"].startswith(mr.PACK_SUBJECT_PREFIX):
|
||||
continue
|
||||
out.setdefault(item["emulator"], []).append(
|
||||
{**item, "open_findings": opened.get(item["emulator"], 0)})
|
||||
return out
|
||||
|
||||
@@ -249,10 +249,44 @@ class SitePages(unittest.TestCase):
|
||||
self.assertIn('<span class="rb-tested-result"><span class="rb-badge rb-badge-success">works</span>',
|
||||
text)
|
||||
self.assertIn('<span class="rb-tested-variants">pack</span>', text)
|
||||
self.assertIn("sega-saturn, Game <small>mednafen_saturn</small>", text)
|
||||
self.assertIn("<strong>Files the core opened:</strong> sega_101.bin (sega_101.bin).", text)
|
||||
self.assertIn("<strong>Findings:</strong> missing_declared x.bin.", text)
|
||||
self.assertIn("sega-saturn, Game <small>mednafen_saturn</small>", text)
|
||||
self.assertIn("<strong>Files the core opened:</strong> sega_101.bin (sega_101.bin).", text)
|
||||
self.assertIn("<strong>Findings:</strong> missing_declared x.bin.", text)
|
||||
self.assertNotIn("| retroarch |", text)
|
||||
|
||||
def test_pack_runs_stay_off_the_emulator_page(self) -> None:
|
||||
import sitemeasurements as sm
|
||||
|
||||
subject = pp.subject_for("retroarch", "pack", "sega-saturn", "mednafen_saturn", "Game (Japan)")
|
||||
self.assertEqual(subject, "pack-retroarch-pack-sega-saturn-mednafen-saturn-game-japan")
|
||||
index = {"items": [{"emulator": "beetle_saturn", "subject": subject},
|
||||
{"emulator": "beetle_saturn", "subject": "boot-sega-101-bin"}]}
|
||||
self.assertEqual([i["subject"] for i in sm.by_emulator(index)["beetle_saturn"]],
|
||||
["boot-sega-101-bin"])
|
||||
|
||||
def test_run_output_is_inert(self) -> None:
|
||||
"""A file name or log line from a third-party core reaches the page
|
||||
as text: no tag, link, code span or MDX expression comes out of it."""
|
||||
import sitemarkup
|
||||
import sitemeasurements as sm
|
||||
|
||||
hostile = "`x` {alert(1)} <script>alert(1)</script> [x](javascript:alert(1)) \"q\" 'q' **b**"
|
||||
item = {"platform": "retroarch", "platform_version": "1", "install": "pack",
|
||||
"system": hostile, "emulator": hostile, "core": hostile, "content": hostile,
|
||||
"verdict": "boots", "date": "2026-10-28T00:00:00+00:00", "opened": 1, "findings": 1,
|
||||
"id": "2026-10-28-0123456789ab", "record_url": "https://example.org/r#L1",
|
||||
"files": [hostile], "finding_list": [hostile], "detail": hostile}
|
||||
text = "\n".join(sm.pack_tested_section([item]))
|
||||
for raw in ("<script", "{alert", "`x`", "[x](", "<code>", "<strong>b", "**b",
|
||||
"href=\"javascript", '"q"', "'q'"):
|
||||
self.assertNotIn(raw, text)
|
||||
self.assertIn("<script>", text)
|
||||
self.assertIn("{alert(1)}", text)
|
||||
# A Markdown link in other site text keeps only http(s) and relative targets.
|
||||
self.assertEqual(sitemarkup.inline_html("[x](javascript:alert(1))"), "[x](javascript:alert(1))")
|
||||
self.assertEqual(sitemarkup.inline_html('[x](https://a.org/"onmouseover=1)'),
|
||||
'<a href="https://a.org/"onmouseover=1">x</a>')
|
||||
self.assertEqual(sitemarkup.inline_html("[x](../a.md)"), '<a href="../a.md">x</a>')
|
||||
self.assertEqual(sm.pack_tested_section([]), [])
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user