From 480a9324261e78c7512b327be7f2eb1195df0966 Mon Sep 17 00:00:00 2001 From: JeffreyChen Date: Fri, 9 Oct 2026 17:07:31 +0800 Subject: [PATCH 1/5] Close the seven SonarCloud issues left on main The Slack report example put message text and the model's summary into the page unescaped, so a message could add markup that made the PDF renderer read a local file; that is what S2083 was pointing at, not the output path. The web viewer's sequential loops are rewritten without an await inside a loop, since NOSONAR is not honoured for script embedded in HTML. --- CHANGELOG.md | 16 ++++ examples/18_slack_daily_report.py | 20 +++- .../remote_desktop/web_viewer/index.html | 96 ++++++++++++------- test/unit_test/headless/test_state_machine.py | 6 +- 4 files changed, 97 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index deeea7fb..22858f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,22 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's ## Unreleased +### Security + +- `examples/18_slack_daily_report.py` escapes the Slack messages and the + model's summary before they go into the report, and gives WeasyPrint a URL + fetcher that loads nothing. Unescaped, a message could add markup that made + the PDF renderer read a local file. + +### Changed + +- Web viewer: sending a file and waiting for the host's offer no longer await + inside a loop. The order of chunks, the 4 MiB backpressure limit and + one-transfer-at-a-time are unchanged; a channel that closes mid-transfer now + ends that transfer with an error instead of leaving it pending. + +## [1.0.1] - 2026-10-09 + ### Changed - `is_package_name` (the gate behind `AC_add_package_to_executor` and diff --git a/examples/18_slack_daily_report.py b/examples/18_slack_daily_report.py index 7ff94288..70e685d8 100644 --- a/examples/18_slack_daily_report.py +++ b/examples/18_slack_daily_report.py @@ -33,6 +33,7 @@ """ from __future__ import annotations +import html import json import os import smtplib @@ -167,6 +168,11 @@ def _path_inside(base_dir: Path, name: str) -> Path: return Path(candidate) +def _refuse_fetch(url: str, *_args: object, **_kwargs: object) -> dict: + """WeasyPrint URL fetcher that loads nothing: the digest has no assets.""" + raise ValueError(f"the digest loads no external resource: {url!r}") + + def render_report(summary: str, raw_messages: Sequence[SlackMessage], output_dir: Path) -> Path: """Render an HTML report + (if weasyprint is available) a PDF. @@ -176,11 +182,14 @@ def render_report(summary: str, raw_messages: Sequence[SlackMessage], """ output_dir.mkdir(parents=True, exist_ok=True) today = datetime.now(tz=timezone.utc).date().isoformat() + # Slack messages and the model's summary are text from outside the + # program: escaped, they cannot add markup (an or that + # would make the PDF renderer open a local file or a URL). rows = "".join( - f"
  • {m.user}: {m.text}
  • " + f"
  • {html.escape(m.user)}: {html.escape(m.text)}
  • " for m in raw_messages ) - html = f""" + page = f""" Daily Slack digest — {today}