platecli controls 3D printers over your local network and downloads
untrusted model files from the internet. Both are security-sensitive, so this
document explains the threat model, the mitigations already in place, known
limitations, and how to report a vulnerability.
Please do not open a public issue for security problems.
Report privately through GitHub's private vulnerability reporting on this repository (Security → Report a vulnerability). If that is unavailable, open a normal issue that says only "security report — please open a private channel" without any details, and a maintainer will follow up.
Please include, where possible:
- affected version (
plate --version) and platform, - a description of the issue and its impact,
- reproduction steps or a proof of concept,
- any known workaround.
You can expect an initial acknowledgement within a few days. Because this is a small volunteer project there is no formal SLA, but confirmed issues will be fixed as a priority and disclosed once a fix is available. Coordinated disclosure is appreciated.
The project is pre-1.0 and under active development. Security fixes are made
against main and land in the next tagged release; there is no long-term
support branch for older versions. Always run the latest release.
| Version | Supported |
|---|---|
latest release / main |
✅ |
| older tags | ❌ |
The tool is designed to run fully locally — there is no cloud account, and no telemetry or data leaves your machine except (a) LAN traffic to your printer and (b) explicit model downloads you request. Key properties:
- Printer TLS. Bambu printers present a self-signed certificate over MQTTS
and FTPS. Pin it with
cert_fingerprint(SHA-256), whichplate setup/doctorcan capture for you. When a fingerprint is set and does not match, the connection is refused on MQTT, FTPS (control + data channel), and the direct camera path. All three transports verify through one shared checker (bambu_cli.tlspin.verify_cert_fingerprint, constant-time compare on normalized hex) so there is a single, audited fail-closed code path rather than three hand-written copies.insecure_tls: truedisables verification entirely and exists only as a last resort — it is never the default and the CLI warns when it is used.- Camera (port 6000): the direct grab refuses to proceed if neither pin nor
insecure_tlsis set. Note thatsnapshotthen falls back to the Docker streamer, which does not honour the pin, so the command as a whole is not fail-closed by default — setcamera_direct_only: trueto refuse that fallback (see Known limitations). - MQTT / FTPS without a pin: use system CA verification (
CERT_REQUIRED), which fails for typical Bambu self-signed certs (effective fail-closed). Prefer an explicit pin for clear errors and uniform policy.
- Camera (port 6000): the direct grab refuses to proceed if neither pin nor
- Access codes are secrets. Store the printer access code in a separate file
via
access_code_file(the recommended path) rather than inline inconfig.json. Inlineaccess_codestill works for legacy configs but is deprecated; migrate withplate setup --migrate-access-code.config showredacts the access code. On POSIX, config and access-code files are tightened toward0600on load. - Downloads are SSRF-hardened. URL fetches (including Printables) resolve
and validate targets before connecting to block requests to private/loopback
address ranges (unless
--allow-private-ips, which is CLI-only and never sticky config), disable environment proxies, cap redirect hops, and enforce a size cap (2048 MB by default,--max-download-mb). - Archive extraction is path-traversal safe. ZIPs are extracted without
allowing entries to escape the destination directory; symlinks are skipped;
existing files are never overwritten (a numbered sibling such as
model-1.stlis created instead). - Printing and other physical/destructive actions require explicit intent. Physical
print start, stop, pause, resume, delete, and raw gcode will not proceed without
--confirm(they refuse with exit code5).job/sendwithout--confirmstill runs download → slice → upload and exits0with"status": "uploaded_not_printed"; only the print step is withheld.--confirmis a deliberate-action gate against accidents, not an authorization boundary: the CLI cannot distinguish a human's flag from an agent's. Agents must not invent confirm flags without user approval.
Documented residuals from the threat model and the 2026-07 security audit. Tracked for hardening; not all are “bugs” in the sense of broken claims.
| Topic | Detail | Status |
|---|---|---|
| Camera Docker port bind | Default camera_port is now 127.0.0.1:1985:1984, so the streamer publishes the (unauthenticated) camera feed on loopback only. Set camera_port to 0.0.0.0:1985:1984 to deliberately expose it on the LAN. Host-qualified specs now parse correctly, camera_port is validated, and the CLI warns if a pre-existing container is still bound to a non-loopback interface (recreate with docker rm -f bambu_camera). |
Fixed |
| Camera pin fallback | A pinned-fingerprint mismatch, and any ssl.SSLError from the direct grab — handshake or post-handshake — hard-abort the snapshot when a pin is configured. The camera_direct_only config key (default false) closes the remaining fallback routes: when set, any failure of the direct port-6000 grab — including no-pin SSLError, non-TLS connection failures (refused/reset/timeout), or a silent no-frame return — refuses to fall back to the Docker streamer and aborts with EXIT_NETWORK_ERROR. What it does not cover: (a) camera_direct_only=true with insecure_tls=true is direct-only but completely unverified — direct-only ≠ verified; verification requires a cert_fingerprint. (b) It stops platecli from using or starting the streamer, but an already-running bambu_camera container keeps serving the unauthenticated feed — run docker rm -f bambu_camera to stop it. (Re-running plate setup used to drop this hand-added key, silently disabling the control; setup now preserves every key it does not manage, and reports which ones it kept.) |
Fixed |
| HTTP downloads | http:// and https:// are both accepted. SSRF controls apply; content integrity over cleartext HTTP does not (a network attacker can substitute a model). Prefer HTTPS sources. |
Residual |
| pause / resume | Required --confirm as of 0.3.0, matching stop/print/delete/gcode. |
Fixed |
| Windows secret ACLs | POSIX 0600 enforcement does not apply on Windows; protect the config directory with NTFS ACLs on shared machines. |
Platform residual |
| Reverse-engineered protocols | MQTT/FTPS behavior is best-effort; firmware updates can break compatibility. | Out of scope |
| Access code = full LAN control | Protect the config directory, especially on agent-operated hosts. | Residual |
| Model content | The tool validates packaging/paths/sizes, not whether a model is safe to print. | Residual |
| TOFU pin capture | First-time fingerprint probe intentionally disables cert verification to read the pin. A MITM during setup can poison the pin if the LAN is already hostile. | Acknowledged |
Agent auto---confirm |
Process/policy issue; code cannot stop intentional confirmation. | Out of process scope |
| Third-party tools | OrcaSlicer, gmsh, and the optional camera Docker image are outside this package’s SBOM boundary. | Out of scope |
| zeroconf / pytest on Python 3.9 | The newest zeroconf and pytest releases that still support Python 3.9 (0.148.0, 8.4.2) carry open moderate advisories, and every patched release requires Python >= 3.10, so they cannot be upgraded on the 3.9 resolution branch. On 3.10+ the dependency floor requires patched zeroconf (see pyproject.toml). Exposure on 3.9 is limited: the advisories are LAN-local DoS / cache-corruption reachable only from a hostile device on the same network, zeroconf is imported lazily and used solely for optional mDNS discovery during plate setup (which degrades gracefully and can be skipped by passing printer details manually), and pytest is a test-only extra that is never installed for end users or shipped in the wheel. Revisit when Python 3.9 support is dropped. |
Accepted (3.9 only) |
In scope: the bambu_cli package and its published wheels/sdist, the CLI
surface, download/extraction handling, and the printer transport layer.
Out of scope: vulnerabilities in third-party dependencies (report those
upstream), OrcaSlicer itself, the printer firmware, and issues that require an
already-compromised local machine or a malicious printer on your own LAN
(beyond what pin/insecure_tls policy can mitigate).
- AGENTS.md — agent safety checklist
- docs/quality-roadmap.md — security scoreboard
- docs/api.md — agent JSON contracts