mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 00:27:20 -05:00
fix: render run output in the Tested block as text
Every value a run produced (claim, method, statement, host, flags, variant labels, core, content and file names) is escaped with quotes and its braces made inert for MDX, never read as Markdown. A record link is written only for an http(s) or relative target. Reference runs fold with the tests from before the oracle.
This commit is contained in:
1 parent
e7de3e5084
commit
dc53f50f67
2 files changed
+69
-19
No files matched your search
+43
-16
@@ -604,7 +604,7 @@ def short_title(item: dict) -> str:
|
|||||||
word = f"issue {issue[1]}" if issue else CHECK_NAMES.get(word, TERMS.get(word, word))
|
word = f"issue {issue[1]}" if issue else CHECK_NAMES.get(word, TERMS.get(word, word))
|
||||||
if word not in axis:
|
if word not in axis:
|
||||||
axis.append(word)
|
axis.append(word)
|
||||||
head = ", ".join(part for part in (f"`{thing}`" if thing else "", game) if part)
|
head = ", ".join(part for part in (thing, game) if part)
|
||||||
title = f"{head} ({' '.join(axis)})" if axis and head else head or " ".join(axis)
|
title = f"{head} ({' '.join(axis)})" if axis and head else head or " ".join(axis)
|
||||||
return title or str(item.get("subject") or "")
|
return title or str(item.get("subject") or "")
|
||||||
|
|
||||||
@@ -758,11 +758,28 @@ def _attr(value: object) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _cell(value: object) -> str:
|
def _cell(value: object) -> str:
|
||||||
"""Record text placed in raw HTML: escaped, code spans kept, a semicolon
|
"""Record text placed in raw HTML: escaped as text, never read as
|
||||||
joining two clauses ending a sentence as on the rest of the site."""
|
Markdown (a run's output is no markup), braces made inert for MDX. A
|
||||||
|
semicolon joining two clauses ends a sentence, as on the rest of the
|
||||||
|
site."""
|
||||||
text = " ".join(str(value or "").split())
|
text = " ".join(str(value or "").split())
|
||||||
text = re.sub(r";\s+(\w)", lambda m: ". " + m[1].upper(), text)
|
text = re.sub(r";\s+(\w)", lambda m: ". " + m[1].upper(), text)
|
||||||
return markup.inline_html(text).translate(_BRACES)
|
return html.escape(text, quote=True).translate(_BRACES)
|
||||||
|
|
||||||
|
|
||||||
|
_SAFE_HREF = re.compile(r"^(?:https?://[^\s\"'<>]+|/[^\s\"'<>]*|\.{0,2}/?[\w.~-][^\s\"'<>:]*)$")
|
||||||
|
|
||||||
|
|
||||||
|
def _href(url: object) -> str | None:
|
||||||
|
"""A link target from a record: http(s) or relative only, else none."""
|
||||||
|
text = str(url or "")
|
||||||
|
return _attr(text) if text and _SAFE_HREF.match(text) and not text.lower().startswith(
|
||||||
|
("javascript:", "data:", "vbscript:")) else None
|
||||||
|
|
||||||
|
|
||||||
|
def _link(url: object, text: str) -> str:
|
||||||
|
target = _href(url)
|
||||||
|
return f'<a href="{target}">{text}</a>' if target else text
|
||||||
|
|
||||||
|
|
||||||
def _dots(item: dict) -> str:
|
def _dots(item: dict) -> str:
|
||||||
@@ -777,7 +794,7 @@ def _dots(item: dict) -> str:
|
|||||||
|
|
||||||
def _chip(item: dict) -> str:
|
def _chip(item: dict) -> str:
|
||||||
words, kind = RESULT_WORDS.get(item["status"], (item["status"], "muted"))
|
words, kind = RESULT_WORDS.get(item["status"], (item["status"], "muted"))
|
||||||
return f'<span class="rb-badge rb-badge-{kind}">{html.escape(words)}</span>'
|
return f'<span class="rb-badge rb-badge-{kind}">{_cell(words)}</span>'
|
||||||
|
|
||||||
|
|
||||||
def _sentence_html(label: str, value: object, markup_ready: bool = False) -> str:
|
def _sentence_html(label: str, value: object, markup_ready: bool = False) -> str:
|
||||||
@@ -788,7 +805,7 @@ def _sentence_html(label: str, value: object, markup_ready: bool = False) -> str
|
|||||||
|
|
||||||
def _details(item: dict) -> list[str]:
|
def _details(item: dict) -> list[str]:
|
||||||
core = item["core"]
|
core = item["core"]
|
||||||
build = _cell(core["library"]) + (f" at <code>{html.escape(core['commit'][:12])}</code>"
|
build = _cell(core["library"]) + (f" at <code>{_cell(core['commit'][:12])}</code>"
|
||||||
if core.get("commit") else "")
|
if core.get("commit") else "")
|
||||||
reps = f"{item['variants']} variant{'s' if item['variants'] != 1 else ''}, " \
|
reps = f"{item['variants']} variant{'s' if item['variants'] != 1 else ''}, " \
|
||||||
f"{item['repetitions']} repetition{'s' if item['repetitions'] != 1 else ''} each"
|
f"{item['repetitions']} repetition{'s' if item['repetitions'] != 1 else ''} each"
|
||||||
@@ -802,7 +819,7 @@ def _details(item: dict) -> list[str]:
|
|||||||
_sentence_html("Proves", ", ".join(item.get("proves") or [])),
|
_sentence_html("Proves", ", ".join(item.get("proves") or [])),
|
||||||
_sentence_html("Ran on", item.get("host_short") or item["host"]),
|
_sentence_html("Ran on", item.get("host_short") or item["host"]),
|
||||||
_sentence_html("Core build", build and f"{build}. {reps}", markup_ready=True),
|
_sentence_html("Core build", build and f"{build}. {reps}", markup_ready=True),
|
||||||
f'<p><a href="{_attr(item["record_url"])}">Full record</a>{runs}.</p>',
|
f'<p>{_link(item["record_url"], "Full record")}{runs}.</p>',
|
||||||
]
|
]
|
||||||
return [line for line in lines if line]
|
return [line for line in lines if line]
|
||||||
|
|
||||||
@@ -823,7 +840,7 @@ def _row(item: dict, title: str, page_host: str, main_core: str) -> list[str]:
|
|||||||
elsewhere = (item.get("host_short") or item["host"]) != page_host
|
elsewhere = (item.get("host_short") or item["host"]) != page_host
|
||||||
title = _cell(title)
|
title = _cell(title)
|
||||||
if core_label(item) != main_core:
|
if core_label(item) != main_core:
|
||||||
title += f' <small>{html.escape(core_label(item))}</small>'
|
title += f' <small>{_cell(core_label(item))}</small>'
|
||||||
return [
|
return [
|
||||||
'<details class="rb-tested-row">',
|
'<details class="rb-tested-row">',
|
||||||
("<summary>"
|
("<summary>"
|
||||||
@@ -849,6 +866,13 @@ def by_file(items: list[dict]) -> list[tuple[str, list[dict]]]:
|
|||||||
return sorted(groups.items(), key=lambda kv: kv[0] == "")
|
return sorted(groups.items(), key=lambda kv: kv[0] == "")
|
||||||
|
|
||||||
|
|
||||||
|
def folded(item: dict) -> bool:
|
||||||
|
"""A row the block folds away: a test from before the boot oracle, or a
|
||||||
|
run that recorded reference frames for the oracle, which a reviewer
|
||||||
|
reads and a visitor does not."""
|
||||||
|
return item["status"] == "no verdict" or str(item.get("subject") or "").startswith("references-")
|
||||||
|
|
||||||
|
|
||||||
def summary_line(items: list[dict], page_host: str) -> str:
|
def summary_line(items: list[dict], page_host: str) -> str:
|
||||||
"""N tests, the confirmed and open counts when there are any, the last
|
"""N tests, the confirmed and open counts when there are any, the last
|
||||||
run and its computer."""
|
run and its computer."""
|
||||||
@@ -880,15 +904,15 @@ def tested_section(name: str, items: list[dict]) -> list[str]:
|
|||||||
f'<a href="{markup.site_href(f"downloads/measurements/{name}.sqlite")}">SQLite</a>')
|
f'<a href="{markup.site_href(f"downloads/measurements/{name}.sqlite")}">SQLite</a>')
|
||||||
ordered = sorted(items, key=lambda i: (i["date"], i["subject"], core_label(i)), reverse=True)
|
ordered = sorted(items, key=lambda i: (i["date"], i["subject"], core_label(i)), reverse=True)
|
||||||
benches = [i for i in ordered if i["kind"] == "bench"]
|
benches = [i for i in ordered if i["kind"] == "bench"]
|
||||||
earlier = [i for i in ordered if i["kind"] != "bench" and i["status"] == "no verdict"]
|
earlier = [i for i in ordered if i["kind"] != "bench" and folded(i)]
|
||||||
current = [i for i in ordered if i["kind"] != "bench" and i["status"] != "no verdict"]
|
current = [i for i in ordered if i["kind"] != "bench" and not folded(i)]
|
||||||
lines = ["## Tested", "", '<div class="rb-tested">',
|
lines = ["## Tested", "", '<div class="rb-tested">',
|
||||||
f'<p class="rb-tested-summary">{html.escape(summary_line(items, page_host))} {links}</p>']
|
f'<p class="rb-tested-summary">{_cell(summary_line(items, page_host))} {links}</p>']
|
||||||
if current or earlier:
|
if current or earlier:
|
||||||
lines.append('<div class="rb-tested-head" aria-hidden="true"><span>Test</span>'
|
lines.append('<div class="rb-tested-head" aria-hidden="true"><span>Test</span>'
|
||||||
"<span>Result</span><span>Variants</span><span>Date</span></div>")
|
"<span>Result</span><span>Variants</span><span>Date</span></div>")
|
||||||
for file, group in by_file(current):
|
for file, group in by_file(current):
|
||||||
label = f"<code>{html.escape(file)}</code>" if file else "No file varied"
|
label = f"<code>{_cell(file)}</code>" if file else "No file varied"
|
||||||
lines.append(f'<p class="rb-tested-file">{label}</p>')
|
lines.append(f'<p class="rb-tested-file">{label}</p>')
|
||||||
for item in group:
|
for item in group:
|
||||||
lines += _row(item, titles[item["id"]], page_host, main_core)
|
lines += _row(item, titles[item["id"]], page_host, main_core)
|
||||||
@@ -897,9 +921,12 @@ def tested_section(name: str, items: list[dict]) -> list[str]:
|
|||||||
'the same colour for the same result. <span class="rb-dot rb-dot-0 rb-dot-empty">'
|
'the same colour for the same result. <span class="rb-dot rb-dot-0 rb-dot-empty">'
|
||||||
"</span> No file staged.</p>")
|
"</span> No file staged.</p>")
|
||||||
if earlier:
|
if earlier:
|
||||||
|
old = sum(1 for i in earlier if i["status"] == "no verdict")
|
||||||
|
parts = [f"{old} test{'s' if old != 1 else ''} from before the boot oracle" if old else "",
|
||||||
|
(f"{len(earlier) - old} reference run{'s' if len(earlier) - old != 1 else ''}"
|
||||||
|
if len(earlier) - old else "")]
|
||||||
lines += ['<details class="rb-tested-earlier">',
|
lines += ['<details class="rb-tested-earlier">',
|
||||||
f"<summary>{len(earlier)} earlier test{'s' if len(earlier) != 1 else ''}, "
|
f"<summary>{_cell(' and '.join(p for p in parts if p))}</summary>"]
|
||||||
"before the boot oracle</summary>"]
|
|
||||||
for item in earlier:
|
for item in earlier:
|
||||||
file = Path(str(item.get("variable") or "")).name
|
file = Path(str(item.get("variable") or "")).name
|
||||||
title = f"{file}, {titles[item['id']]}" if file else titles[item["id"]]
|
title = f"{file}, {titles[item['id']]}" if file else titles[item["id"]]
|
||||||
@@ -913,9 +940,9 @@ def tested_section(name: str, items: list[dict]) -> list[str]:
|
|||||||
timing = item.get("timing") or {}
|
timing = item.get("timing") or {}
|
||||||
title = _cell(titles[item["id"]])
|
title = _cell(titles[item["id"]])
|
||||||
if core_label(item) != main_core:
|
if core_label(item) != main_core:
|
||||||
title += f' <small>{html.escape(core_label(item))}</small>'
|
title += f' <small>{_cell(core_label(item))}</small>'
|
||||||
table.append(
|
table.append(
|
||||||
f'<tr><td><a href="{_attr(item["record_url"])}">{title}</a></td>'
|
f'<tr><td>{_link(item["record_url"], title)}</td>'
|
||||||
f'<td>{timing.get("ms_per_frame", 0):.3f}</td><td>{timing.get("fps", 0):.1f}</td>'
|
f'<td>{timing.get("ms_per_frame", 0):.3f}</td><td>{timing.get("fps", 0):.1f}</td>'
|
||||||
f'<td>{timing.get("stdev", 0):.3f}</td><td>{item["repetitions"]}</td></tr>')
|
f'<td>{timing.get("stdev", 0):.3f}</td><td>{item["repetitions"]}</td></tr>')
|
||||||
lines.append("".join([*table, "</tbody></table>"]))
|
lines.append("".join([*table, "</tbody></table>"]))
|
||||||
|
|||||||
@@ -458,17 +458,17 @@ class SitePages(unittest.TestCase):
|
|||||||
cases = [
|
cases = [
|
||||||
({"subject": "mpeg-card-lunar-cr2-build", "variable": "mpeg_card.bin",
|
({"subject": "mpeg-card-lunar-cr2-build", "variable": "mpeg_card.bin",
|
||||||
"content": "Redump: Lunar - Silver Star Story Complete (Japan), T-27904G V1.00"},
|
"content": "Redump: Lunar - Silver Star Story Complete (Japan), T-27904G V1.00"},
|
||||||
"`mpeg_card.bin`, Lunar (CR2 build)"),
|
"mpeg_card.bin, Lunar (CR2 build)"),
|
||||||
({"subject": "negative-scph5501-bin-mednafen-psx-bust-a-move-2-arcade-edition-usa",
|
({"subject": "negative-scph5501-bin-mednafen-psx-bust-a-move-2-arcade-edition-usa",
|
||||||
"variable": "scph5501.bin", "content": "Bust-A-Move 2 - Arcade Edition (USA)",
|
"variable": "scph5501.bin", "content": "Bust-A-Move 2 - Arcade Edition (USA)",
|
||||||
"core": {"file": "mednafen_psx_libretro.so"}},
|
"core": {"file": "mednafen_psx_libretro.so"}},
|
||||||
"`scph5501.bin`, Bust-A-Move 2 (wrong files)"),
|
"scph5501.bin, Bust-A-Move 2 (wrong files)"),
|
||||||
({"subject": "issue85-lunar-opening-movie", "variable": None,
|
({"subject": "issue85-lunar-opening-movie", "variable": None,
|
||||||
"content": "Redump: Lunar - Silver Star Story Complete (Japan), T-27904G V1.00"},
|
"content": "Redump: Lunar - Silver Star Story Complete (Japan), T-27904G V1.00"},
|
||||||
"Lunar (issue 85 opening movie)"),
|
"Lunar (issue 85 opening movie)"),
|
||||||
({"subject": "with-command-auth-device-and-command-get-9a211715", "variable": "mpeg_card.bin",
|
({"subject": "with-command-auth-device-and-command-get-9a211715", "variable": "mpeg_card.bin",
|
||||||
"content": "Redump: Lunar"},
|
"content": "Redump: Lunar"},
|
||||||
"`mpeg_card.bin`, Lunar (with command auth device and get)"),
|
"mpeg_card.bin, Lunar (with command auth device and get)"),
|
||||||
]
|
]
|
||||||
for item, title in cases:
|
for item, title in cases:
|
||||||
self.assertEqual(sm.short_title(item), title)
|
self.assertEqual(sm.short_title(item), title)
|
||||||
@@ -498,6 +498,29 @@ class SitePages(unittest.TestCase):
|
|||||||
# Alone in its file, a test is named by its game.
|
# Alone in its file, a test is named by its game.
|
||||||
self.assertEqual(sm.distinct_titles(items[:1]), {"a": "Lunar"})
|
self.assertEqual(sm.distinct_titles(items[:1]), {"a": "Lunar"})
|
||||||
|
|
||||||
|
def test_run_output_is_text_never_markup(self) -> None:
|
||||||
|
nasty = "`code` {expr} <script>alert(1)</script> [x](javascript:alert(1)) \"q\" 'q' **b**"
|
||||||
|
item = self.item(
|
||||||
|
claim=nasty, method=nasty, statement=nasty, host=nasty, host_short=nasty, content=nasty,
|
||||||
|
variable=f"{nasty}.bin", subject="s", title=nasty, proves=[nasty], runs=2, flags=1,
|
||||||
|
core={"library": nasty, "commit": "{`<x>`}", "file": f"{nasty}_libretro.so"},
|
||||||
|
groups=[{"variant": nasty, "group": 0, "result": nasty, "absent": False}],
|
||||||
|
record_url="javascript:alert(1)")
|
||||||
|
bench = self.item(id="b", kind="bench", status="measured", subject="bench", title=nasty,
|
||||||
|
content=nasty, record_url="java\nscript:x",
|
||||||
|
timing={"ms_per_frame": 1.0, "fps": 1.0, "stdev": 0.0})
|
||||||
|
earlier = self.item(id="e", status="no verdict", subject="e", content=nasty, claim=nasty)
|
||||||
|
text = "\n".join(sm.tested_section("alpha", [item, bench, earlier]))
|
||||||
|
self.assertNotIn("<script", text)
|
||||||
|
self.assertNotRegex(text, r'href="(?!https?://|/)')
|
||||||
|
self.assertNotIn("javascript:alert(1)\"", text)
|
||||||
|
self.assertNotRegex(text, r"[{}]")
|
||||||
|
self.assertNotIn("<strong>b</strong>", text)
|
||||||
|
self.assertNotIn("<code>code</code>", text)
|
||||||
|
self.assertNotRegex(text, r'="[^"]*"q"')
|
||||||
|
self.assertIn("<script>", text)
|
||||||
|
self.assertIn("{expr}", text)
|
||||||
|
|
||||||
def test_no_empty_sentence(self) -> None:
|
def test_no_empty_sentence(self) -> None:
|
||||||
text = "\n".join(sm.tested_section("beetle_saturn", [self.item(claim=None, method=None, statement="",
|
text = "\n".join(sm.tested_section("beetle_saturn", [self.item(claim=None, method=None, statement="",
|
||||||
kind="run", status="ran")]))
|
kind="run", status="ran")]))
|
||||||
|
|||||||
Reference in new issue
Block a user