Skip to content
GitHub
documentation

Security

How Bloom handles sign-in, tokens, stored secrets, uploads and its audit log, and how to report a vulnerability.

Bud and Bloom are built to run in production. Runner registration is credential-based and guarded by a separate station enrolment key passed through RUNNER_API_KEY; secrets stay in the local identity vault, never in your repository; each test class is isolated in its own process with enforced timeouts; and the daemon control socket is loopback-only by default. Those runner controls are on the Bud side and described in the Bud docs. Bloom protects its own sessions, stored secrets, uploads and audit records.

If you find a vulnerability, follow the repository’s SECURITY.md: responsible disclosure to dev@embedlabs.net. Do not open a public GitHub issue for a suspected vulnerability.

Include:

  • the affected deployment or version;
  • reproduction steps or a proof of concept;
  • an impact assessment;
  • any suggested mitigation.

EmbedLabs acknowledges receipt as soon as practical and coordinates next steps over email. Give EmbedLabs reasonable time to investigate and prepare a fix before public disclosure. Paid priority-support customers should also use their contracted support channel. Security fixes are applied to the latest supported release.

  • In production, Bloom rejects a missing or weak SECRET_KEY and requires an administrator password of at least 16 characters.
  • Passwords must be at least 12 characters. Changing or resetting a password signs out all existing sessions. Completing an email change ends existing sessions too.
  • Access tokens last 30 minutes and refresh tokens 30 days by default. Auth cookies are sent only over HTTPS in production (AUTH_COOKIE_SECURE).
  • Signing out, and a session check that fails while a user is signed in, reload the page. A tab also reloads when it next renews its session and finds it belongs to another user signed in from another tab. Nothing cached for the previous user stays in memory, and signing in empties the cache as well.
  • One-time links (invitation, email verification, password reset, email change) carry their token only in the URL fragment and are single-use, keeping tokens out of request targets, server logs, and the Referer header.
  • First-run setup at /setup answers 409 once any user exists.
  • Bud result sync. Bloom issues Bud a scoped test-results:write credential that lasts 90 days and can be revoked. It is shown once. Bloom hashes the service tokens it issues with SERVICE_TOKEN_PEPPER. See Connecting to Bud.
  • Issue trackers. GitHub, GitLab, and Jira credentials and webhook secrets are encrypted at rest (Fernet) with INTEGRATION_ENCRYPTION_KEY and are never returned through the API. Bloom remains fully usable without the key; only tracker credential operations fail closed.
  • Webhooks. Inbound deliveries must be authenticated, and each delivery identifier is accepted once, so a captured request cannot be replayed. See Issue tracker sync.

Keep SECRET_KEY, SERVICE_TOKEN_PEPPER, INTEGRATION_ENCRYPTION_KEY, and the SMTP credentials in your secret store. A different SECRET_KEY invalidates sessions and pending invite and reset links; a different SERVICE_TOKEN_PEPPER invalidates Bud result-sync credentials; a different INTEGRATION_ENCRYPTION_KEY makes configured tracker secrets unreadable until they are rotated.

  • The interactive API docs are off by default (ENABLE_DOCS=false). Enable them only in a trusted development environment: they expose the full schema and every endpoint to anyone who can reach the host.
  • /api/metrics is unauthenticated, following the Prometheus convention. Keep it reachable only from your monitoring network, or turn it off with ENABLE_METRICS=false.
  • Set CORS_ORIGINS to your public URL in production.
  • Attachments. Bloom limits file size, total size per document and upload rate, keeps a free-space reserve, validates an allowlist of MIME types, streams each upload through a temporary file, and promotes only a complete upload.
  • ReqIF. Archives are validated while streaming and parsed in a time-limited worker process, within limits on size, archive entries, compression ratio, object and relation counts, hierarchy depth, request rate, and one active import per project.

These controls limit malformed or resource-intensive input. They are not a malware scanner. The limits are in the configuration reference.

Security-relevant events are written to the audit_events table in the same transaction as the action:

  • sign-in, sign-out, session refresh, password change and reset, email verification and email change;
  • user creation, invitation, role change, activation and deletion;
  • project members, external document types, groups, policies and artefact visibility;
  • configuration changes;
  • deletions, exports, imports and attachments.

A failed sign-in or password reset is recorded even though the request itself fails. Each event carries the acting user or anonymous, service or runner, the action, the target, the request ID (the x-request-id response header), the client address and user agent, the outcome and details without secrets. The client address is the one the image’s own nginx received. Behind another reverse proxy (Cloudron, a load balancer) it is that proxy’s address.

Administrators read the log under Audit log in the sidebar, or with GET /api/audit, filtered by actor, action or action prefix, target, project, outcome and time. Event times are shown in the viewer’s time zone. No endpoint changes or deletes an event.

Retention is your choice. Bloom never deletes events. To remove old ones, run this on a schedule:

python -m app.audit prune --older-than-days 365

Every release build publishes an SPDX SBOM (bloom-sbom.spdx.json) alongside the container image. CI runs Bandit, pip-audit, and blocking npm audit checks on every push.