Redact printer access codes from support bundle logs

RTSP stream URLs (rtsps://bblp:<code>@<ip>:322/...) were not covered
  by the credential sanitizer, leaking access codes in support bundles
  and bug report logs. Extended the URL regex to match rtsps:// and added
  access codes to the sensitive string collection for exact-match
  redaction in both export paths.
This commit is contained in:
maziggy
2026-03-15 08:42:17 +01:00
parent 5a8aa61f44
commit 79f4a62308
2 changed files with 12 additions and 7 deletions
+1
View File
@@ -33,6 +33,7 @@ All notable changes to Bambuddy will be documented in this file.
### Security
- **PyJWT ≥2.12.0** — Bumped minimum version to address CVE-2026-32597.
- **flatted ≥3.4.0** — Updated transitive ESLint dependency to address GHSA-25h7-pfq9-p65f (unbounded recursion DoS).
- **Access Code Redacted from Support Logs** — Printer access codes embedded in RTSP stream URLs were not redacted in support bundles and bug report logs. Extended the URL credential sanitizer to cover `rtsps://` URLs and added access codes to the sensitive string collection for exact-match redaction.
### Changed
- **CI: Node.js 20 → 22** — Updated GitHub Actions workflows (`ci.yml`, `security.yml`) from Node.js 20 to Node.js 22 LTS ahead of [GitHub's Node 20 deprecation](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/).
+11 -7
View File
@@ -735,8 +735,8 @@ def _sanitize_log_content(content: str, sensitive_strings: dict[str, str] | None
continue # Skip very short strings to prevent over-redaction
content = re.sub(re.escape(value), label, content)
# Replace credentials in URLs (e.g. http://user:pass@host)
content = re.sub(r"(https?://)[^/:@\s]+:[^/@\s]+@", r"\1[CREDENTIALS]@", content)
# Replace credentials in URLs (e.g. http://user:pass@host, rtsps://bblp:code@host)
content = re.sub(r"((?:https?|rtsps?)://)[^/:@\s]+:[^/@\s]+@", r"\1[CREDENTIALS]@", content)
# Replace email addresses
content = re.sub(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", "[EMAIL]", content)
@@ -786,14 +786,16 @@ async def _get_recent_sanitized_logs(max_lines: int = 200) -> str:
# Collect sensitive strings from DB for redaction
sensitive_strings: dict[str, str] = {}
async with async_session() as db:
result = await db.execute(select(Printer.name, Printer.serial_number, Printer.ip_address))
for name, serial, ip_address in result.all():
result = await db.execute(select(Printer.name, Printer.serial_number, Printer.ip_address, Printer.access_code))
for name, serial, ip_address, access_code in result.all():
if name:
sensitive_strings[name] = "[PRINTER]"
if serial:
sensitive_strings[serial] = "[SERIAL]"
if ip_address:
sensitive_strings[ip_address] = "[IP]"
if access_code:
sensitive_strings[access_code] = "[ACCESS_CODE]"
result = await db.execute(select(User.username))
for (username,) in result.all():
@@ -839,15 +841,17 @@ async def generate_support_bundle(
# Collect known sensitive values for log redaction
sensitive_strings: dict[str, str] = {}
# Printer names, serial numbers, and IP addresses
result = await db.execute(select(Printer.name, Printer.serial_number, Printer.ip_address))
for name, serial, ip_address in result.all():
# Printer names, serial numbers, IP addresses, and access codes
result = await db.execute(select(Printer.name, Printer.serial_number, Printer.ip_address, Printer.access_code))
for name, serial, ip_address, access_code in result.all():
if name:
sensitive_strings[name] = "[PRINTER]"
if serial:
sensitive_strings[serial] = "[SERIAL]"
if ip_address:
sensitive_strings[ip_address] = "[IP]"
if access_code:
sensitive_strings[access_code] = "[ACCESS_CODE]"
# Auth usernames
result = await db.execute(select(User.username))