Skip to content

All versions since v3.1.0

v3.1.0 Latest

What’s new

This is a security-hardening release. It closes an unauthenticated deploy-trigger path in the incoming webhook endpoints, escapes every user-influenced value that reaches a shell command (closing command-injection paths, including one on the control-plane host), fixes a cross-environment access-control gap on the database monitoring routes, and adds SSRF guards to the registry and outgoing-webhook clients. It also ships dashboard-topology rendering fixes and expanded documentation. No database migrations.

If you use the incoming webhook endpoints (/api/webhooks/*), read “Action required” below before upgrading — they now fail closed.


Action required before upgrading

Skip this section if you don’t use the incoming CI/CD webhook endpoints.

Incoming webhooks now fail closed

The /api/webhooks/deploy, /api/webhooks/deploy-image, and /api/webhooks/github endpoints now require a configured secret and a valid signature. Previously the signature was verified only when a secret happened to be set (and the GitHub endpoint could be bypassed entirely by omitting the signature header). If the relevant secret is not set, the endpoint is now disabled and returns 401.

Before upgrading, make sure the secret is configured on your BridgePort instance:

Terminal window
# Generate a strong secret and set it in your environment / compose:
openssl rand -base64 32
# WEBHOOK_SECRET=... (for /api/webhooks/deploy and /deploy-image)
# GITHUB_WEBHOOK_SECRET=... (for /api/webhooks/github)

Your CI must sign the exact request body bytes it sends (HMAC-SHA256, hex; the GitHub endpoint uses the sha256= prefix). See docs/guides/webhooks.md. If you don’t use these endpoints, no action is needed. (#351)


Security

This release resolves findings from an internal security review. The webhook issue is the only one reachable without authentication; the command-injection and SSRF paths require an authenticated operator (BridgePort’s role gate already blocks read-only “viewer” accounts from the operations that reach them), and the IDOR affects environment-scoped API tokens.

  • Unauthenticated webhook deploy-trigger / signature bypass — the GitHub webhook verified its signature only when the header was present, so omitting it skipped verification even with a secret configured; the other webhook endpoints verified only when a secret was set. All three now fail closed (secret + valid signature required), HMAC is computed over the raw request bytes, and the comparison is length-safe. Malformed payloads and image tags containing shell metacharacters are rejected. (#351)
  • Command-injection hardening in shell exec paths — every user-influenced value interpolated into a shell command (container name, image reference, health-check URL, database-backup connection fields, SSH monitoring placeholders) is now shell-escaped, and config/compose files are written over SFTP instead of shell heredocs. (#351)
  • Cross-environment access control (IDOR) — the database metrics, test-connection, and monitoring sub-routes now verify the database belongs to the environment in the request path, so an environment-scoped token can no longer reach databases in other environments. (#351)
  • SSRF guards — the container-registry client and the admin outgoing-webhook delivery path now block private/loopback/link-local/cloud-metadata addresses and no longer follow redirects; registryUrl must be a valid URL. (#351)
  • Defense-in-depth — baseline security response headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy), an explicit secret-scrub on outbound Sentry events, and a startup warning when JWT_SECRET is weak. (#351)

Tracked as follow-ups (not in this release): hashing agent tokens at rest, explicit per-route role gates on the service/registry routes, and per-account login throttling.


Fixes

Dashboard topology rendering (#335, #336, #337)

  • Services with deployments on multiple servers now render under every server they run on in the dashboard topology diagram, instead of only one. (#335, #336)
  • Saved server-box sizes are clamped to their children’s footprint, so a manually-resized server box no longer clips the services inside it. (#337)

Documentation

  • “What’s Next” roadmap page — the docs site now has a /roadmap/ page generated at build time from the GitHub milestones and epics, plus a discoverable manual redeploy workflow for refreshing it. (#349)
  • Maintainer runbook — a new docs/development/maintainer-runbook.md consolidating release, CI, tracker, and naming conventions. (#350)
  • Docs-site build & deploy documentation and general discoverability improvements. (#315, #334)

Under the hood

  • Release → docs refreshrelease.yml now pings the Cloudflare Pages deploy hook after cutting a release, so the docs changelog refreshes without waiting for the next push to master. (#314)
  • Dependency-group bumps: #331, #332, #333. -----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgEyiv4hf6iBgr34ICjN6HnEP/vs Yr31eNU5HhdkQaYd4AAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQNdqD+8jBq0JCSd88RMP81EGZNPSM0s0KvG5Xa0UWCVsx0Ii8D8rfvQ1qr//f4IH52 Dk1u7+dyoKVwWppZ27vg8= -----END SSH SIGNATURE-----