From 74691fd036736c9eb97b25312abaf5fadf94594a Mon Sep 17 00:00:00 2001 From: maziggy Date: Tue, 19 May 2026 13:58:21 +0200 Subject: [PATCH 1/9] Bumped version --- CHANGELOG.md | 2 +- backend/app/core/config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6675c5ab..4c8920abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to Bambuddy will be documented in this file. -## [0.2.5b1] - Unreleased +## [0.2.4.2] - 2026-05-19 ### Added - **Docker: opt-in system trust store for self-signed CA certificates (#1431, contributed by @WizBangCrash, requested in #1289)** — Reporter runs a private LAN with self-signed certificates for internal HTTPS endpoints (his Home Assistant instance being the canonical case) and wanted Bambuddy to trust those CAs without disabling TLS verification end-to-end. Bambuddy talks to Home Assistant via `httpx.AsyncClient` (`backend/app/services/homeassistant.py:46`) with default `verify=True`, which under httpx 0.28 means "use `certifi`'s CA bundle and nothing else" — so manually copying a CA file into the container had no effect. **The fix is opt-in and container-side only**: setting `USE_SYSTEM_TRUST_STORE=` in the compose `environment:` block, combined with mounting the user's CA file(s) into `/usr/local/share/ca-certificates`, makes the entrypoint run `update-ca-certificates --fresh` at startup and `export SSL_CERT_DIR=/etc/ssl/certs`. httpx 0.28 explicitly honours that env var (`_config.py`: `ssl.create_default_context(capath=os.environ["SSL_CERT_DIR"])`), and `update-ca-certificates` populates `/etc/ssl/certs` with the **Debian system CA bundle** (Let's Encrypt, DigiCert, GlobalSign, etc.) **plus** the user-mounted CAs — so standard endpoints (api.github.com, MakerWorld, Bambu Cloud) keep working alongside the user's self-signed CA. The `ca-certificates` apt package is added to the Dockerfile so `update-ca-certificates` exists in the image. The feature is **default-off** — when the env var is unset the entrypoint logs a one-line "skipping system trust store update" and goes straight to the existing PUID/PGID chown path, so non-users see zero behaviour change. **Fail-fast on misconfig**: if `USE_SYSTEM_TRUST_STORE` is set but the container is running as non-root (the entrypoint can't write `/etc/ssl/certs` without root), or `/usr/local/share/ca-certificates` has no `.crt` files mounted, or `update-ca-certificates` is missing from the image, or the trust-store rebuild itself fails, the entrypoint exits 1 with a clear error message rather than silently succeeding and leaving the user wondering why their HA connection still rejects the cert. **Compose template update**: `docker-compose.yml` ships commented-out examples for both the volume mount (`/path/to/certs:/usr/local/share/ca-certificates`) and the env var (`USE_SYSTEM_TRUST_STORE=true`) so the path from "I have a self-signed CA" to "Bambuddy trusts it" is two uncommented lines. **Caveat worth flagging in docs**: the feature requires the container to start as root so the entrypoint can run `update-ca-certificates`; users who pin `user: "1000:1000"` in compose get the clear "not running as root" exit with the reason, but they need to switch to the default PUID/PGID-style invocation to use this. Companion wiki PR documents the setup walkthrough at maziggy/bambuddy-wiki#31. Hardware-only path (shell entrypoint change) so no automated test — verified by the reporter's local install. Post-merge polish: the fatal-exit branch's log line was relabeled from "warning: update-ca-certificates failed:" to "error: update-ca-certificates failed" to match severity and the surrounding error messages. diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 2fab2f3dc..09b92c6d4 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -6,7 +6,7 @@ from pathlib import Path from pydantic_settings import BaseSettings # Application version - single source of truth -APP_VERSION = "0.2.5b1" +APP_VERSION = "0.2.4.2" GITHUB_REPO = "maziggy/bambuddy" BUG_REPORT_RELAY_URL = os.environ.get("BUG_REPORT_RELAY_URL", "https://bambuddy.cool/api/bug-report") From 162db57923d3918c6818614eaeb34d95c101e000 Mon Sep 17 00:00:00 2001 From: maziggy Date: Tue, 19 May 2026 14:17:31 +0200 Subject: [PATCH 2/9] chore(security): nosec false-positive Bandit findings in tests PR #1434 CI flagged 5 B402 (ftplib import) in test_bambu_ftp.py and 2 B108 (hardcoded /tmp) in test_print_start_assigns_printer_id_to_vp_archive.py. Both are intentional in tests: the FTP client tests need real ftplib exception classes to construct mock 426 responses, and the /tmp path is a MagicMock attribute never written to. Marked with `# nosec B402` / `# nosec B108` plus a one-line justification each, matching the convention from c2630399. --- backend/tests/unit/services/test_bambu_ftp.py | 10 +++++----- ...est_print_start_assigns_printer_id_to_vp_archive.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/tests/unit/services/test_bambu_ftp.py b/backend/tests/unit/services/test_bambu_ftp.py index 6ccb2c1c7..10e52bd11 100644 --- a/backend/tests/unit/services/test_bambu_ftp.py +++ b/backend/tests/unit/services/test_bambu_ftp.py @@ -397,7 +397,7 @@ class TestUpload: succeeding on the printer side (v0.2.4.1 worked because the prior proceed-with-warning branch tolerated the noise). """ - import ftplib + import ftplib # nosec B402 — tests need the real ftplib to construct mock 426 responses local = tmp_path / "test.bin" local.write_bytes(b"data" * 256) # 1024 bytes @@ -427,7 +427,7 @@ class TestUpload: isn't on the server at full size (or SIZE itself fails), the upload must fail so the dispatcher doesn't send a print command for a partial 3MF.""" - import ftplib + import ftplib # nosec B402 — tests need the real ftplib to construct mock 426 responses local = tmp_path / "test.bin" local.write_bytes(b"data" * 256) @@ -452,7 +452,7 @@ class TestUpload: """If SIZE itself fails (e.g. server too broken to answer), assume the worst and fail — better a retry than a print on a partial file. """ - import ftplib + import ftplib # nosec B402 — tests need the real ftplib to construct mock 426 responses local = tmp_path / "test.bin" local.write_bytes(b"data" * 256) @@ -474,7 +474,7 @@ class TestUpload: def test_upload_bytes_426_with_intact_file_proceeds(self, ftp_client_factory, ftp_server): """upload_bytes() mirrors the same SIZE-verify logic as upload_file.""" - import ftplib + import ftplib # nosec B402 — tests need the real ftplib to construct mock 426 responses client = ftp_client_factory() client.connect() @@ -495,7 +495,7 @@ class TestUpload: def test_upload_bytes_426_with_truncated_file_returns_false(self, ftp_client_factory, ftp_server): """The truncated branch for upload_bytes().""" - import ftplib + import ftplib # nosec B402 — tests need the real ftplib to construct mock 426 responses client = ftp_client_factory() client.connect() diff --git a/backend/tests/unit/test_print_start_assigns_printer_id_to_vp_archive.py b/backend/tests/unit/test_print_start_assigns_printer_id_to_vp_archive.py index 18e9a549a..54fa0dff8 100644 --- a/backend/tests/unit/test_print_start_assigns_printer_id_to_vp_archive.py +++ b/backend/tests/unit/test_print_start_assigns_printer_id_to_vp_archive.py @@ -64,7 +64,7 @@ async def test_expected_archive_path_assigns_printer_id_when_unset(): mock_archive.printer_id = None mock_archive.print_name = "A1 Tool Plate 3" mock_archive.status = "archived" - mock_archive.file_path = "/tmp/fake.3mf" + mock_archive.file_path = "/tmp/fake.3mf" # nosec B108 — mock path; nothing ever writes to it mock_archive.energy_start_kwh = None register_expected_print(1, "bambu_lab_a1_tool_plate_3.gcode.3mf", archive_id=42, ams_mapping=None) @@ -151,7 +151,7 @@ async def test_expected_archive_path_preserves_existing_printer_id(): mock_archive.printer_id = 7 # already correct mock_archive.print_name = "MyModel" mock_archive.status = "archived" - mock_archive.file_path = "/tmp/fake.3mf" + mock_archive.file_path = "/tmp/fake.3mf" # nosec B108 — mock path; nothing ever writes to it mock_archive.energy_start_kwh = None register_expected_print(7, "MyModel.3mf", archive_id=99, ams_mapping=None) From 6da7d1edeb905841fcda80a786c0ecdfd4c1d6ac Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 20 May 2026 11:39:44 +0200 Subject: [PATCH 3/9] Updated BACKERS.md --- BACKERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BACKERS.md b/BACKERS.md index f51f97fec..0a80ef6b0 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -28,6 +28,7 @@ If you sponsor and your name isn't here within 48h, please write an email to mar - [@rewart01](https://github.com/rewart01) - [@rstocks](https://github.com/rstocks) - [@sixfootseven](https://github.com/sixfootseven) +- [@pwostran](https://github.com/pwostran) ## Backers ($5/mo+) @@ -36,7 +37,6 @@ If you sponsor and your name isn't here within 48h, please write an email to mar - [@grizz0blaw](https://github.com/grizz0blaw) - [@NoahTingey](https://github.com/NoahTingey) - [@sentinel-center](https://github.com/sentinel-center) -- [@pwostran](https://github.com/pwostran) --- ## One-time and historical supporters From fdd20e49b3db30425aafc4eaaa765e27ad0de2f5 Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 20 May 2026 12:36:32 +0200 Subject: [PATCH 4/9] chore(security): bump idna >=3.15 (CVE-2026-45409) + ignore disputed PyJWT advisory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - requirements.txt: pin idna>=3.15 to clear ReDoS in idna.encode() on crafted Unicode payloads. Transitive via anyio/httpx/requests/yarl, so the explicit floor stops a future downstream loosening from silently downgrading us. - security.yml: permanently --ignore-vuln CVE-2025-45768 (PyJWT). The advisory is disputed by the maintainers — "key length is chosen by the application" — and no fix version exists. Bambuddy is safe: auto-generates secrets via secrets.token_urlsafe(64) and rejects file-loaded secrets shorter than 32 chars (auth.py:177, :184). - security.yml: drop the stale Pygments --ignore-vuln CVE-2026-4539. Pygments has been patched upstream; the ignore no longer matches anything. --- .github/workflows/security.yml | 15 +++++++++++---- CHANGELOG.md | 6 ++++++ requirements.txt | 5 +++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index fd02de967..7033440ab 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -132,10 +132,17 @@ jobs: - name: Run pip-audit id: pip-audit run: | - # CVE-2026-4539: low-severity ReDoS in Pygments AdlLexer (indirect dep via mkdocs-material/pytest/rich). - # No fix available yet. Remove --ignore-vuln once Pygments releases a patched version. - pip-audit --desc on --format json --output pip-audit-results.json --ignore-vuln CVE-2026-4539 || echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT - pip-audit --desc on --ignore-vuln CVE-2026-4539 || true + # CVE-2025-45768 (PYSEC-2025-183 / GHSA-65pc-fj4g-8rjx): disputed by PyJWT maintainers. + # Advisory says "key length is chosen by the application that uses the library" — no + # PyJWT fix exists or will exist. Bambuddy is safe: backend/app/core/auth.py:184 uses + # secrets.token_urlsafe(64) (~86 chars of entropy) for auto-generated secrets and + # rejects file-loaded secrets shorter than 32 chars at :177. Keep ignored permanently. + pip-audit --desc on --format json --output pip-audit-results.json \ + --ignore-vuln CVE-2025-45768 \ + || echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT + pip-audit --desc on \ + --ignore-vuln CVE-2025-45768 \ + || true - name: Upload audit results if: always() diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c8920abf..a133692fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to Bambuddy will be documented in this file. +## [0.2.4.3] - Unreleased + +### Security +- **idna: bump to `>=3.15` to clear CVE-2026-45409 (ReDoS in `idna.encode()` with crafted Unicode payloads, e.g. `"٠" * N` or `"・" * N + "漢"`)** — Transitive dep pulled in by anyio / httpx / requests / yarl; not directly pinned, which is why it lingered at 3.13. Added an explicit `idna>=3.15` floor in `requirements.txt` between Authentication and HTTP-client blocks with a comment explaining why it's pinned (so a future downstream loosening doesn't silently downgrade us). Verified via `pip-audit` clean post-upgrade. +- **PyJWT CVE-2025-45768 (PYSEC-2025-183 / GHSA-65pc-fj4g-8rjx): permanently ignored in pip-audit** — Advisory is disputed by the PyJWT maintainers, with the advisory description literally noting *"this is disputed by the Supplier because the key length is chosen by the application that uses the library."* `fix_versions=[]` on the advisory confirms no PyJWT patch exists or will exist. Bambuddy is not affected: `backend/app/core/auth.py:184` auto-generates secrets via `secrets.token_urlsafe(64)` (~86 chars of entropy, far above any sane minimum) and the file-loaded path at `:177` rejects secrets shorter than 32 chars. Added a permanent `--ignore-vuln CVE-2025-45768` to `.github/workflows/security.yml` with an inline comment citing the file:line evidence so a future maintainer reviewing the ignore list sees why it's load-bearing. Also dropped the stale `--ignore-vuln CVE-2026-4539` for Pygments — Pygments has since shipped a patched version and the ignore is no longer load-bearing (verified: `pip-audit --ignore-vuln CVE-2025-45768` alone reports clean). + ## [0.2.4.2] - 2026-05-19 ### Added diff --git a/requirements.txt b/requirements.txt index 4a2762129..3c855b1a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -60,6 +60,11 @@ passlib[bcrypt]>=1.7.4 ldap3>=2.9.0 pyotp>=2.9.0 +# Transitive dep pin: idna<3.15 has CVE-2026-45409 (ReDoS on encode() with +# crafted Unicode). Pulled in by anyio/httpx/requests/yarl; pin the floor +# so we don't regress when a downstream loosens its constraint. +idna>=3.15 + # HTTP client (used for OIDC token exchange) httpx>=0.26.0 From 90f68820328747ef6af7ed2efb3c3ff2ffc78473 Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 20 May 2026 12:43:10 +0200 Subject: [PATCH 5/9] Updated .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index ffaee2ce3..3c83decb2 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,5 @@ advertisements/ # gitleaks reports gitleaks-report.json + +scripts/pip-audit.sh From 93118b9c3e3c3f77333a7ac33cd41fe18fff8644 Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 20 May 2026 13:21:59 +0200 Subject: [PATCH 6/9] chore(ci): also ignore disputed PyJWT CVE-2025-45768 in ci.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit security.yml had this ignore added in 9d440beb but ci.yml runs its own pip-audit step with a separate ignore list. CI was still failing on main + dev. Reasoning identical to the security.yml comment — disputed by PyJWT maintainers, no fix exists, Bambuddy uses secrets.token_urlsafe(64) and rejects short secrets. --- .github/workflows/ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 493404ebd..005307934 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,15 @@ jobs: run: | # CVE-2026-4539: low-severity ReDoS in Pygments AdlLexer (indirect dep via mkdocs-material/pytest/rich). # No fix available yet. Remove --ignore-vuln once Pygments releases a patched version. - pip-audit --desc on --ignore-vuln CVE-2026-4539 + # + # CVE-2025-45768 (PYSEC-2025-183 / GHSA-65pc-fj4g-8rjx): disputed by PyJWT maintainers. + # Advisory says "key length is chosen by the application that uses the library" — no + # PyJWT fix exists or will exist. Bambuddy is safe: backend/app/core/auth.py:184 uses + # secrets.token_urlsafe(64) (~86 chars of entropy) for auto-generated secrets and + # rejects file-loaded secrets shorter than 32 chars at :177. Keep ignored permanently. + pip-audit --desc on \ + --ignore-vuln CVE-2026-4539 \ + --ignore-vuln CVE-2025-45768 backend-tests: name: Backend Tests From be669a7aeab1db48b4c102fabdaf4af6f0ff8d1a Mon Sep 17 00:00:00 2001 From: maziggy Date: Thu, 21 May 2026 11:01:53 +0200 Subject: [PATCH 7/9] Updated .github/ISSUE_TEMPLATE/bug_report.yml and .github/ISSUE_TEMPLATE/config.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 ++ .github/ISSUE_TEMPLATE/config.yml | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 58332a262..8a8ef0fc3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -172,3 +172,5 @@ body: required: true - label: My printer has Developer Mode enabled required: true + - label: For a connection or printing problem, I ran the in-app Connection Diagnostic (printer card or System page) and included the result above + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c3cb5d3b8..e873cd805 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,11 @@ blank_issues_enabled: false contact_links: + - name: Printer won't connect or won't print? + url: https://wiki.bambuddy.cool/reference/troubleshooting/ + about: Most connection and printing problems are setup issues. Check the troubleshooting guide and run the in-app Connection Diagnostic (printer card or System page) before opening an issue. - name: Documentation - url: https://github.com/maziggy/bambuddy-wiki - about: Check the documentation for guides and troubleshooting + url: https://wiki.bambuddy.cool/ + about: Setup guides, feature documentation, and reference. - name: Community Forum url: https://forum.bambuddy.cool - about: Ask questions and share ideas with the community + about: Ask questions and share ideas with the community. From 6d673d062624387a699bde1a885d82e605395a97 Mon Sep 17 00:00:00 2001 From: maziggy Date: Sat, 23 May 2026 08:05:20 +0200 Subject: [PATCH 8/9] Updated BACKERS --- BACKERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BACKERS.md b/BACKERS.md index 0a80ef6b0..e93775ce2 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -29,6 +29,7 @@ If you sponsor and your name isn't here within 48h, please write an email to mar - [@rstocks](https://github.com/rstocks) - [@sixfootseven](https://github.com/sixfootseven) - [@pwostran](https://github.com/pwostran) +- [@MethodicalMartian](https://github.com/MethodicalMartian) ## Backers ($5/mo+) From cc468ddd42fbb09878fceb2805a288e1235640d7 Mon Sep 17 00:00:00 2001 From: maziggy Date: Sun, 24 May 2026 09:45:42 +0200 Subject: [PATCH 9/9] Updated BACKERS.md --- BACKERS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BACKERS.md b/BACKERS.md index e93775ce2..8fe0293c2 100644 --- a/BACKERS.md +++ b/BACKERS.md @@ -38,6 +38,8 @@ If you sponsor and your name isn't here within 48h, please write an email to mar - [@grizz0blaw](https://github.com/grizz0blaw) - [@NoahTingey](https://github.com/NoahTingey) - [@sentinel-center](https://github.com/sentinel-center) +- [@brianehlert](https://github.com/brianehlert) +- [@siiruup](https://github.com/siiruup) --- ## One-time and historical supporters