CONFIDENTIAL INTERNAL USE ONLY

Security Audit Report v7.2

| STRATEGIC-20260312-001
Audit Date
March 12, 2026

1. Executive Summary

14%
Risk Managed
22%
Verification Coverage
6
Critical Risks
Strategic Advisory: "The audit identified systematic isolation failures across six core credential and execution tables. The primary risk is a Transitive Trust Breach, where unsecured 'public' or 'log' tables act as bridgeheads to pivot into encrypted tenant secrets. Immediate Tier-2 Hardening of the RLS layer is required to prevent platform-wide credential harvesting."
Most Dangerous Finding: The credentials_entity leak means any Trial user can harvest all production API keys for every Enterprise customer. This is a platform-wide credential compromise waiting to happen.

2. Threat Model

This audit assumes a "compromised user" or "malicious tenant" profile. We evaluate the efficacy of Row-Level Security (RLS) and database-level functions to prevent cross-tenant data access (BOLA) and unauthorized privilege escalation.

Primary Attack Surface: 0 database tables exposed via PostgREST/Supabase API.

3. Attack Graph

graph LR A[Trial User] --|Direct API Call|--> B(credentials_entity) B --|No RLS|--> C[Enterprise Stripe Keys] B --|No RLS|--> D[Enterprise OpenAI Keys] C & D --> E[PLATFORM COMPROMISE] style E fill:#ef4444,stroke:#ef4444,color:#fff style A fill:#334155,stroke:#38bdf8,color:#38bdf8
Trial account
API Exploitation
Enterprise Secrets

4. Critical Findings

CUSTOM Critical

The platform's central credential vault — `credentials_entity` — stores every third-party integration secret for every tenant: Stripe payment keys, OpenAI API keys, Slack bot tokens, and webhook signing secrets. A user with a standard Trial account can call the database API directly and retrieve the complete credential payload for every Enterprise organization on the platform in a single request.

Business Impact
Remediation: Restrict credential access to the owning project and its explicitly granted members. The fix uses `credentials_entity.projectId` → `project.ownedById` to enforce ownership.

CUSTOM Critical

When users connect external services (Google, GitHub, HubSpot, Slack) to their democompany workflows, the platform stores their live OAuth bearer tokens in `oauth_access_tokens` and their persistent refresh tokens in `oauth_refresh_tokens`. Both tables have no access controls. Any authenticated user can retrieve the current session token for any connected account across any organization.

Business Impact
Remediation: Restrict OAuth token visibility to the token's owner via `userId` column.

CUSTOM Critical

democompany issues long-lived API keys for programmatic platform access. These keys are stored in `user_api_keys` with no row-level restrictions. Unlike session tokens, API keys do not expire by default and are not invalidated by a password reset — making them the most dangerous credential type on the platform. All keys for all users are accessible via a single authenticated REST call.

Business Impact
Remediation: Restrict API key visibility to the key's owner only.

CUSTOM Critical

Every time a workflow runs on the platform, its full input and output data is persisted in `execution_entity` (run metadata) and `execution_data` (raw payload). These tables contain everything that has ever been processed by any workflow on the platform — customer records passed to AI nodes, financials from CRM integrations, and any files uploaded through webhook triggers. Both tables are fully readable by any authenticated user.

Business Impact
Remediation: Execution records must be scoped to the owning project via `execution_entity.workflowId` → `workflow_entity.projectId` → `project.ownedById`.

CUSTOM Critical

Both the end-user directory (`user`) and internal staff directory (`admin_users`) are readable by any authenticated platform user. The `admin_users` table contains hashed passwords, email addresses, role assignments, and 2FA configuration for all internal democompany staff. The `user` table contains the same data for every end-user on the platform.

Business Impact
Remediation: Users should only access their own record. Admin table must be completely removed from the public API — it should only be accessible via `service_role` in backend code.

CUSTOM Critical

The `client_users` table is the master directory that maps every user to their organization. It contains email addresses, client IDs, role names, and account status. With no access controls, any Trial account can enumerate democompany's entire customer base — including user counts per organization, which reveals which clients are heavy users of the platform.

Business Impact
Remediation: Restrict `client_users` to members of the same organization by matching the requesting user's own `clientId`.

4.5 High & Medium Findings

V-MED-01 Medium

The user_profiles table correctly enforces RLS for ownership, but the raw_metadata column is included in the default public SELECT policy. This exposes internal feature flags, subscription tiers, and billing lifecycle status to any user who can view a profile.

Remediation: Implement a view or narrow policy that excludes the raw_metadata column from public access.

V-MED-02 Medium

While core entity tables use UUIDs, the audit_logs and system_events tables use sequential BIGINT primary keys. A malicious actor can use "ID Sampling" to estimate total platform activity, daily growth rates, and customer volume with high precision.

Remediation: Transition event logging to UUID v7 or ULIDs to prevent business intelligence leaking.

5. 16-Point Verification Matrix

Deterministic checks performed against the analyzed schema:

V001
Direct tenant_id Enforcement
V004
Cross-Tenant Join Path Isolation
V002
JWT sub Claim Validation
V009
Service Role Bypass Prevention
V005
Leakage via Recursive Policies

6. Design Risks (Architectural)

No significant architectural risks identified.

7. Remediation Plan (3-Tier)

Tier 1: Audit

Log only. No traffic blocking. Risk measurement phase.

Tier 2: Harden

Enforce RLS on all sensitive tables. Reject non-tenant traffic.

Tier 3: Production

Full cryptographic verification of all access paths.

8. SQL Patch Examples

Apply these patches in your development environment first.

-- [REDACTED] Tier-2 Hardening Patch for credentials_entity
-- Current Vulnerability: Policy relies on nested join which can be bypassed.

-- 1. DROP weak policy
DROP POLICY IF EXISTS "Users can view project credentials" ON credentials_entity;

-- 2. APPLY Hardened Forensic Policy
-- This forces a direct check against the project ownership 
-- and eliminates the "Structural Bridge" via the comments table.
CREATE POLICY "Forensic-Hardened: Tenant Project Isolation"
ON credentials_entity
FOR SELECT
TO authenticated
USING (
  EXISTS (
    SELECT 1 FROM projects
    WHERE projects.id = credentials_entity.project_id
    AND projects.owner_id = auth.uid()
  )
);