Security
DriftWise is designed around defense in depth — no single control is the only thing standing between an attacker and your data. This page summarises the controls we run in production. Exact implementation details (file paths, library versions, private endpoints, threat-model specifics) live in internal documentation; what follows is the posture we expect any security-minded customer to scrutinise.
Summary
| Area | Control |
|---|---|
| Edge | TLS-only, edge rate limiting on authentication paths; frontend, auth, and telemetry hostnames proxied through Cloudflare with origin locked down to Cloudflare IP ranges |
| Identity | OIDC, SAML SSO (Team+), SCIM (Enterprise), role-based access control |
| API authentication | HMAC-peppered API keys with scopes |
| Tenant isolation | Database Row-Level Security enforced with FORCE, dedicated least-privilege database role |
| Encryption in transit | HTTPS everywhere, managed database configured to reject plaintext connections, HSTS with preload |
| Encryption at rest | Cloud-provider-managed disk encryption, cluster Secrets wrapped with a customer-managed key, cloud credentials and result bodies wrapped with AES-256-GCM and bound to tenant via AAD before storage |
| Application defenses | SSRF allowlisting, LLM prompt-injection envelope, XSS sanitisation, strict CSP, request size limits |
| Platform | Strict admission controls, seccomp RuntimeDefault, hardened node images, keyless cloud-API authentication |
| Supply chain | SAST, SCA, SBOM generation, vulnerability scanning on every PR |
| Operations | Tamper-evident audit log with per-org SHA-256 hash chain, structured logs, distributed tracing, managed database point-in-time recovery |
Network and transport
- TLS-only in transit. All customer traffic reaches DriftWise over HTTPS. The managed database accepts encrypted connections only — plaintext handshakes are refused at the server.
- Edge protection — split posture. The frontend
(
app.driftwise.ai), authentication broker (auth.driftwise.ai), and telemetry ingest (rum.driftwise.ai) are proxied through Cloudflare. For those three hostnames the origin is locked down at the GCP Gateway to Cloudflare's published IP ranges, so the direct-IP bypass path is closed — all legitimate traffic arrives via the edge. Cloudflare provides TLS termination, HSTS, and rate limiting on the OAuth callback path for these hostnames. The API surface (api.driftwise.ai) is intentionally exposed directly: API-key clients — CI/CD pipelines, the DriftWise Kubernetes operator, and inbound provider webhooks — need an unrestricted path. Its origin-side defenses (HTTPS, HSTS, per-route rate limiting, API-key + OIDC auth, body-size caps, SSRF guards) are described elsewhere on this page. - Private database. The production database has no public IP — it is reachable only from inside the private network the application runs on.
- Restricted control plane. The cluster's control-plane API is gated to an allowlist of operator IP ranges.
- HSTS with preload. Strict-Transport-Security is served with a
long max-age,
includeSubDomains, andpreloadso that standards-compliant browsers refuse to downgrade to HTTP.
Identity and access
- OIDC sign-in. User authentication is handled through an identity broker that issues signed, short-lived JWTs. The backend validates tokens against the broker's public keys on every request.
- SAML SSO. Customers on the Team plan and above can connect their own identity provider via any SAML 2.0–compliant service (Okta, Entra ID, Google Workspace, JumpCloud, and others). See SSO & SCIM.
- SCIM provisioning. Enterprise customers can automate user and group lifecycle via SCIM.
- Role-based access control. Each organization has
owner,admin,member, andviewerroles. Security-sensitive mutations (billing, identity provider configuration, role reassignment) require OIDC-backedowner/admin— API keys cannot perform them regardless of scope. - API keys.
- Keys are hashed with HMAC-SHA256 and a server-side pepper. Only the prefix is stored in plaintext for display; the full key is shown to the user exactly once.
- Keys carry scopes (
read/write) that are enforced at the middleware layer. Security-critical mutations — billing, identity provider configuration, role reassignment, API-key revocation — are blocked for all API keys regardless of scope and require a live OIDC session (see RBAC above). - Revocation requires a human. API keys cannot revoke other
API keys — the
DELETE /api/v2/orgs/:id/api-keys/:idendpoint accepts only OIDC-backed owner/admin identities. This closes a lateral-movement path where a compromised write-scoped key could rotate itself or lock out the real owner. - Revocations publish to a shared revocation bus so every backend pod invalidates its cache within one round-trip; a revoked key stops working within seconds, not within the cache TTL.
Tenant isolation
Tenant data is isolated at multiple independent layers — losing any one of them should not leak rows or objects.
Database tier (relational data)
- Application filter. Every repository query that touches a
tenant table includes an explicit
WHERE org_id = $Nclause. - Database Row-Level Security with
FORCE. Every tenant table has an RLS policy keyed on a per-transaction session variable.FORCE ROW LEVEL SECURITYis applied so the table owner does not silently bypass the policy. - Least-privilege database role. The backend connects as a
role that owns nothing, has no direct privileges on tenant
tables, and cannot inherit privileges from administrative roles.
Every query enters a transaction-scoped role switch that carries
the caller's
org_id; a query that escapes that wrapper returns "permission denied" rather than silently running unscoped.
The combination means a missed WHERE clause no longer leaks data
— RLS catches it. A compromised policy does not leak data either —
the application filter still runs. And a raw-pool query that
bypasses both still fails because the connecting role lacks
privileges.
Object-storage tier (scan results and compliance packs)
Scan results, analysis responses, and compliance-pack bundles live in object storage rather than the relational database. They are isolated by four independent controls:
- Typed tenant boundary. The storage API accepts a distinct
OrgIDtype (not a plain string). Call sites must perform an explicit conversion at the authenticated-tenant boundary, so every place tenant identity crosses into the storage layer is grep-visible and auditable. - Tenant-scoped key layout. Every object lands under an
<orgID>/prefix. The backend validatesorgIDagainst an allowlist regex before composing the path, so attacker-controlled characters (/,.., control bytes) cannot escape the prefix. - Per-tenant envelope encryption. Every object body is encrypted with AES-256-GCM. The tenant identifier and object key are bound to the ciphertext via GCM Additional Authenticated Data (AAD). A raw bucket read at the wrong prefix, a moved or renamed object, or a rewrite of the plaintext header all fail authentication — the server returns "not found" rather than leaking another tenant's content.
- Loud-failure detection. Cloud Storage data-access audit logs are enabled on the result bucket. A log-based alert fires on any access by a principal outside the expected allowlist, so a mistakenly-granted IAM role or a stolen credential surfaces within minutes rather than during the next audit.
The envelope layer in particular means that even an attacker who fully compromises the backend service account — enough to issue raw object reads against the bucket — reads ciphertext bound to the wrong AAD and cannot decrypt it.
Encryption
- In transit. HTTPS for all client and internal traffic. The managed database is set to accept TLS-encrypted connections only.
- At rest.
- Customer data lives on cloud-provider-managed persistent storage with envelope encryption enabled by default.
- Cluster Secrets are additionally wrapped with a customer- managed key before being written to the cluster store. Every Secret decrypt is logged in the key management service's audit logs.
- Cloud credentials (the AWS/Azure/GCP credentials you attach for scanning) are encrypted with AES-256-GCM using a separate application-layer key before being written to the database. A database-only leak does not expose them.
- Scan results, analysis responses, and compliance-pack bundles stored in object storage are wrapped in an AES-256-GCM envelope whose ciphertext is bound to the tenant and object key via GCM Additional Authenticated Data. An attacker with raw read access to the bucket sees ciphertext that cannot be decrypted outside its intended tenant context — moving, renaming, or misattributing an object fails authentication. See Tenant isolation → Object-storage tier above for the full control set.
- Backups. Production databases have automated backups and point-in-time recovery enabled.
Application defenses
- SSRF protection. Every user-supplied URL (webhook endpoints, custom LLM endpoints, state-source URIs) is validated against a public-HTTPS allowlist before any request goes out. Private, loopback, link-local, CGNAT, and metadata-service addresses are rejected. Production boot refuses to start if this guard is bypassed — there is no per-request escape hatch.
- LLM prompt-injection envelope. Untrusted content sent to an LLM (Terraform plan JSON, resource names, webhook bodies, etc.) is wrapped in a clearly-delimited envelope. The paired system prompt asserts the envelope contract so the model distinguishes trusted instructions from untrusted data.
- LLM output is never auto-applied. Generated remediation and Terraform code are returned to a human for review. There is no path in the product where a model's response is automatically applied to infrastructure.
- XSS sanitisation. Markdown rendered in the UI is sanitised with an allowlist sanitiser before being inserted into the DOM.
- Strict Content Security Policy. The UI ships with a narrow
CSP — no inline scripts, no eval, no
object-src, noframe-src, with per-environment allowlists for network destinations. The CSP is generated at build time and covered by regression tests that guard against accidental loosening. - Request body size limits. All HTTP endpoints enforce a hard request-body size cap.
- Rate limiting. Authentication paths and expensive endpoints are rate-limited at the edge (on Cloudflare-proxied hostnames) and at the origin. Origin rate limiting uses a Redis-backed counter shared across backend pods, so per-IP and per-user budgets are enforced cluster-wide rather than per-replica. Platform-LLM usage and plan analyses are separately budgeted via plan-tiered weekly and hourly quotas.
- Inbound webhooks. Webhooks are authenticated with HMAC
signatures and protected against replay by a delivery-dedupe
table. Replayed bodies return
200 already_processedwithout re-running side effects.
Platform hardening
- Strict admission controls. The application namespace rejects privileged, host-path, and capability-bearing containers at the admission controller.
- Seccomp
RuntimeDefault. Every application container runs under the runtime's default seccomp profile. - Hardened node images. Cluster nodes run with Secure Boot and integrity monitoring enabled.
- Keyless cloud-API authentication. Pods authenticate to cloud APIs via short-lived, federated credentials; no long-lived service-account keys live inside the cluster.
- Supply-chain-verified images. Every container image is built in CI (not on developer laptops), produces an SBOM, and is scanned for known vulnerabilities before being promoted. Production SBOMs are archived to durable storage for provenance.
Supply chain
Every pull request runs, as blocking checks:
- Dependency vulnerability scanning. Backend and frontend dependencies are checked against public vulnerability databases, and SBOMs are scanned against known CVEs on every build.
- Static analysis. Bearer SAST runs against the full source
tree on every pull request. The Go backend gets an additional
layer via
gosec(security-focused SAST) andstaticcheck(correctness lint). Any high-severity finding blocks the merge. - SBOM generation. Software bills of materials are produced for backend and frontend on every build. Production releases archive their SBOM to durable storage.
- Integration tests against a real database. Every PR that touches the backend runs the full integration suite against an ephemeral database container — exercising real row-level security, real schema migrations, and real tenant isolation.
- End-to-end tests. A separate E2E suite drives the real server binary and the real UI via browser automation on every PR.
- CI-built artifacts only. Production container images are built and pushed by the CI/CD pipeline — never from a developer's laptop. The deploy pipeline is gated on all of the above.
Observability and auditability
- Tamper-evident audit log. Security-sensitive mutations — API
key create and revoke, cloud account changes, billing state
changes, identity provider swaps, membership and role changes,
SCIM provisioning — write to an append-only audit log. Each row
joins a per-organization SHA-256 hash chain: every row references
the hash of its predecessor, so any retroactive mutation or
deletion of past entries breaks the chain. Database-level
RESTRICTIVErow-security policies forbidUPDATEandDELETEfrom the application role, so the chain also grows append-only under a compromised handler. Owners, admins, and auditors can independently verify the chain on demand viaGET /api/v2/orgs/:id/audit-log/verify, which walks every row, recomputes each hash, and returnsokorbrokenwith the first broken sequence number. Entries record the actor, action, target, and timestamp; secrets and raw credentials are never logged. See Audit Logs for the full event catalog, verify protocol, and auditor-handoff workflow. - Structured application logs. Every request is logged with a request ID, HTTP method, path, status, and latency.
- Distributed tracing. HTTP handlers emit OpenTelemetry spans for end-to-end request tracing; downstream calls (database, LLM providers, cloud APIs) inherit the request context so their latency shows up on the same trace.
- Platform audit logs. Key-management decrypt events, IAM changes, and database admin operations are captured by the cloud provider's audit logging.
- PII scrubbing in telemetry. Client-side telemetry strips emails, UUIDs, JWTs, API keys, and cloud access key IDs before events leave the browser.
Response and recovery
- Point-in-time recovery. Production databases retain backups with PITR enabled.
- Rollback playbook. Every deploy can be reverted via the pipeline without manual surgery.
- Key rotation. Database passwords, cluster Secrets, and the Secret-envelope customer-managed key can be rotated under the documented runbook. The API-key HMAC pepper and webhook-signing secrets are single-value today: rotating them is a customer-visible event (all API keys must be reissued, all outbound webhook subscribers must pick up the new secret). The rollout of a dual-secret verification path for zero-downtime pepper and webhook-secret rotation is tracked on our internal roadmap.
Responsible disclosure
Found something? Email [email protected]. We respond within one business day and will work with you to coordinate disclosure.
What's not on this page
This page describes production security. Items we are actively tracking but have not yet shipped — IAM-based database authentication, database mTLS, CSP violation reporting, image digest pinning — are managed on our internal roadmap. We'll move them to this page once they land.