This document describes how GrandFlow (Open Grant Flow) handles a suspected security incident or data breach, and how to report a vulnerability. It covers the platform's own operations; if you self-host GrandFlow, you are responsible for incident response on your own deployment.
If you believe you've found a security vulnerability, please report it privately rather than opening a public GitHub issue. Use GitHub's private vulnerability reporting feature on this repository, or open a draft security advisory. Please include:
- A description of the vulnerability and its potential impact
- Steps to reproduce it
- Any relevant logs, screenshots, or proof-of-concept code
We aim to acknowledge reports within 5 business days.
A suspected incident can surface through several channels: an alert from
the production observability stack (see docs/observability/), a report
from a user or subprocessor, an unusual pattern noticed during routine
review, or a vulnerability report as above. Anyone on the team who
suspects a breach should treat it as a live incident immediately rather
than waiting for confirmation — see step 2.
- Establish, at a high level, what data or systems may be affected (e.g., which service, which database, whether personal data such as names/emails/financial records is in scope).
- Contain the exposure as the immediate priority. Depending on the
situation this may mean: revoking the credential or API key involved,
forcing re-authentication (session revocation — see the
session-securitycapability, which makes this possible for the first time as of this change), rotating the shared JWT signing secret, disabling the affected endpoint, or blocking network access to the affected host. - Preserve evidence (logs, database snapshots) before remediating where feasible, so root cause can still be established afterward.
- Determine the root cause, the timeline (when the exposure started and when it was contained), and the scope (which records/accounts were actually accessed or could have been accessed, not just theoretically reachable).
- Production logs and traces are exported to Grafana Cloud (see
docs/observability/GRAFANA_CLOUD_PRODUCTION.md) and are the primary source for reconstructing a timeline.
- If the investigation confirms personal data was affected, GDPR's 72-hour notification clock (Article 33) starts from when the breach was first confirmed, not from when it began. Assess without delay whether the incident is reportable to the relevant supervisory authority.
- If the breach is likely to result in a high risk to affected individuals, they must also be notified directly (Article 34), in clear language describing what happened and what they should do.
- Track who is responsible for each notification (regulator vs. affected users vs. any affected subprocessor) as a checklist, not left implicit.
- Fix the underlying cause, not just the immediate symptom.
- Write a short post-incident summary: what happened, root cause, what changed to prevent recurrence. Keep this internal unless disclosure is otherwise required.
GrandFlow superusers can start a time-boxed impersonation session scoped to any customer, from a customer-search control in the top navigation bar (visible only to superusers). The resulting session grants admin-equivalent read/write access to that customer's account, for support, demo, security, and compliance purposes — without asking for or storing that customer's credentials.
Two properties make this auditable rather than an unaccountable backdoor:
- True-actor attribution. The session carries the superuser's own
real identity, not one borrowed from the impersonated customer's
users — every
created_by/updated_byfield recorded during the session reflects who actually performed the action. - Centralized, non-opt-in audit logging. Every request made under an impersonation session — reads and writes alike — is logged (actor, target customer, method, path, timestamp) by a shared hook in every service's request path, not by per-route instrumentation that a new endpoint could forget to add. Audit-log write failures block the request for mutations (fail-closed); a broken audit table does not block plain page views (fail-open), so a reliability issue can't silently disable the audit trail for writes while still letting a demo session read data.
Sessions expire after a short, fixed lifetime
(IMPERSONATION_TOKEN_EXPIRE_MINUTES), and every active session shows
a persistent, non-dismissible banner naming the impersonated customer,
with no way to hide it short of ending the session. See the
customer-impersonation and privileged-access-audit capabilities for
the full behavior.
This process covers GrandFlow's own hosted platform and its
subprocessors (see docs/security/subprocessors.md). It does not cover
self-hosted deployments, which are operated independently.