How We Secure Your Competitive Intelligence Data
A transparent look at VantageDash's security architecture — from encrypted credentials to NIST 800-53 controls.
Your Pricing Data Is Sensitive. We Treat It That Way.
Competitive intelligence platforms sit in an uncomfortable position. They hold some of your most sensitive business data — what you sell, what you charge, who your competitors are, and how your pricing compares. A breach doesn't just expose data. It hands your strategy to the competition.
Most CI tools bury their security posture behind vague "enterprise-grade" claims. We'd rather show you exactly what we've built.
Multi-Tenant Isolation at the Database Level
Every row of data in VantageDash is scoped to your tenant. Not at the application level — at the database level, using Supabase Row Level Security (RLS).
Every table that stores customer data has a tenant_id column enforced by a PostgreSQL function called get_user_tenant_id(). This function reads your tenant from the JWT token on every single query. There's no application code path that can accidentally return another tenant's data, because the database itself refuses to serve it.
This isn't a middleware check that a developer might forget to add. It's a database constraint that applies to all 20+ data tables, including competitors, products, scrape sessions, alerts, billing events, and team memberships.
Encrypted Credentials at Rest
When you connect your Shopify store, we need to store your access token. That token never touches the database in plaintext.
We encrypt all third-party credentials using Fernet symmetric encryption — AES-128-CBC with HMAC-SHA256 authentication. The encryption key is derived from your deployment's service role key via SHA-256, or from a dedicated CREDENTIAL_ENCRYPTION_KEY environment variable if you set one. Every token is encrypted before insert and decrypted only at the moment it's needed for an API call.
The Fernet format includes a timestamp and initialization vector per token, so even two identical credentials produce different ciphertext.
Rate Limiting That Actually Makes Sense
Blanket rate limits are lazy. A GET request to load your dashboard shouldn't count against the same budget as a DELETE request to wipe your data.
VantageDash uses tiered token-bucket rate limiting per client IP:
- Read requests: 120/minute (browsing your dashboard shouldn't feel throttled)
- Mutations (POST/PUT/PATCH): 30/minute (reasonable for active use)
- Auth-sensitive endpoints: 10/minute (credential management gets tighter limits)
- Destructive actions (data deletion): 5/minute (deliberate friction on irreversible operations)
Health checks and webhook endpoints are unlimited — monitoring tools and payment processors should never get throttled. Each response includes X-RateLimit-Remaining headers so your integrations can back off gracefully instead of hitting walls.
Audit Trail on Every Mutation
Every POST, PUT, PATCH, and DELETE request to the VantageDash API generates a structured audit log entry with:
- Request ID (UUID4) — unique identifier returned via
X-Request-IDheader for end-to-end tracing - Body hash — SHA-256 digest of the request payload (truncated to 16 hex characters) for tamper detection
- Client IP and User-Agent — who made the request and from where
- Authentication status — whether a valid JWT was present
- Duration — how long the request took, in milliseconds
Sensitive fields like passwords, API keys, and tokens are automatically redacted before logging. The body hash lets you verify after the fact that a logged request matches what was actually sent — non-repudiation without storing sensitive payloads.
Security Headers on Every Response
Both the frontend and backend set security headers on every HTTP response:
Transport security: HSTS with a 1-year max-age,includeSubDomains, and preload directive. Once your browser visits VantageDash, it will refuse non-HTTPS connections for a full year.
Content Security Policy: Strict CSP that limits script execution to our domain, blocks frame embedding (frame-ancestors 'none'), and restricts form submissions to same-origin. Third-party resources are explicitly allowlisted — Supabase for auth, Google Fonts for typography, and that's about it.
Browser protections: X-Content-Type-Options: nosniff (no MIME sniffing), X-Frame-Options: DENY (no clickjacking), Permissions-Policy disabling camera, microphone, and geolocation APIs that a pricing tool has no business accessing.
Cache control: no-store on all API responses. Your competitive data is never cached by intermediate proxies.
Container Hardening
The backend runs in a hardened Docker container:
- Non-root execution: The application runs as
appuser, a restricted user with no shell and no home directory. Even if an attacker achieves code execution, they land in a minimal environment with no privilege escalation path. - Minimal image: Python 3.12-slim base. Build tools (gcc, g++) are installed for compilation, then purged from the final image.
- Pinned dependencies: All Python packages are version-pinned in
requirements.txtfor reproducible builds.
Automated Security Scanning
Our CI pipeline includes five layers of automated security checks:
Beyond CI, we run CodeQL static analysis for both JavaScript/TypeScript and Python, and Dependabot monitors all dependency trees for known vulnerabilities.
NIST 800-53 Alignment
We map our security controls to NIST 800-53 Rev. 5 — the same framework used by U.S. federal agencies. Our current mapping covers 39 controls across 10 families:
- Access Control (AC): RLS tenant isolation, least-privilege containers, role-based team access
- Audit & Accountability (AU): Structured mutation logging, request tracing, non-repudiation via body hashing
- Security Assessment (CA): 2,200+ automated tests (unit, integration, e2e, fuzz, load, security)
- Configuration Management (CM): Hardened Docker baseline, dependency pinning, build tool removal
- System & Communications Protection (SC): HSTS, CSP, rate limiting, Fernet encryption at rest
- System Integrity (SI): Gitleaks pre-commit scanning, Trivy vulnerability scanning, structured diagnostics
Physical security, personnel security, and media protection are managed by our infrastructure providers (Supabase, Vercel, Coolify) and are out of scope for our application-level mapping.
2,250+ Automated Tests
Security isn't just about locks — it's about knowing they work. With 2,250+ automated tests across 6 layers (unit, integration, e2e, fuzz, load, security), our test suite includes:
- Tenant isolation tests that verify one tenant's queries never return another tenant's data
- JWT validation tests covering expired tokens, malformed tokens, and missing auth headers
- Input sanitization tests for SQL injection, XSS, and command injection payloads
- Chaos and fault injection tests that simulate database failures, API quota exhaustion, and concurrent write conflicts
- Cryptographic validation tests verifying encryption round-trips and key derivation
- Property-based fuzzing (Hypothesis) that generates random inputs to find edge cases humans wouldn't think to test
Every PR runs against this full suite. Zero failures is the deployment gate.
The Bottom Line
We built VantageDash for businesses that take competitive intelligence seriously — and that means taking security seriously too. Every feature described here is in the codebase today, not on a roadmap. If you're evaluating CI tools and security matters to your team, we're happy to walk through any of these controls in detail.