Security

Security at VerifyWise

Security is central to VerifyWise's open-source AI governance platform. We're committed to secure development throughout our entire lifecycle with a transparent, community-driven approach.

Our security framework

Comprehensive security practices built into every aspect of our platform

Secure development practices

Automated vulnerability scanning, license violation checks, and commit reviews to detect credential leakage using GitHub for secure source code hosting.

Continuous improvement

Values community feedback through open-source model allowing security experts to identify vulnerabilities. Security issues reported to security@verifywise.ai.

Compliance & legal

Committed to GDPR and privacy laws. Works with Data Protection Officer and maintains compliance with security standards.

Deployment & monitoring

Incorporates automated testing, provides deployment security guidelines, and continuous vulnerability monitoring.

Application security

Data encryption in rest and transit, role-based access controls (RBAC), and restricted system access.

Transparency

Open-source security philosophy that welcomes professional contributions for faster vulnerability addressing.

Additional security measures

Comprehensive security practices across all aspects of development

Community security guidelines

Secure coding practices required with detailed pull request review process.

Dependency management

GitHub tools scan third-party dependencies with routine dependency checks and updates.

Code review process

Internal and community code reviews with automated and manual security checks.

Open governance

Trusted contributor selection and monitoring of maintainer and contributor integrity.

Open source security philosophy

Our open-source approach enables security experts worldwide to identify vulnerabilities and contribute to improvements. This transparent model allows for faster vulnerability addressing and professional community contributions.

  • Community-driven security improvements
  • Transparent vulnerability disclosure
  • Professional security expert contributions
  • Faster security issue resolution

Security contact

Found a security issue? We value responsible disclosure and community feedback.

Security overview

Platform security overview

An evidence-based account of security controls in the VerifyWise codebase. Every claim references specific source files. Where controls are partial or absent, this document says so.

Version 1.0 ยท February 2026 ยท Public

Executive summary

VerifyWise is an open-source AI governance platform for managing compliance frameworks, model inventories, risk assessments, vendor due diligence, and policy lifecycles. Deployed self-hosted, organizations retain full control over their data and infrastructure.

Tenant isolation

Schema-per-tenant architecture using SHA-256 derived identifiers, ensuring complete data separation between organizations.

Authentication

JWT-based access tokens (1-hour lifetime) with separate refresh tokens (30-day lifetime, httpOnly cookies), verified through a 7-layer middleware chain.

Authorization

Role-based access control with four roles (Admin, Reviewer, Editor, Auditor), enforced at the middleware layer with real-time database cross-checks.

Encryption

AES-256-CBC with per-operation random initialization vectors for sensitive fields; bcrypt for password hashing.

Input validation

Custom validation framework combined with parameterized SQL queries throughout.

Rate limiting

Tiered rate limits across authentication, general API, file operations, and AI scan endpoints.

Dependency scanning

Weekly Dependabot scans across npm, pip, GitHub Actions, and Docker ecosystems with CI-enforced audit checks.

Docker Compose for development and small-scale deployments; Kubernetes with Kustomize for production environments.

Architecture overview

Technology stack, multi-tenancy model, and deployment patterns

Technology stack

ComponentTechnologyVersion
Backend APINode.js / Express / TypeScriptNode 24 (Alpine)
DatabasePostgreSQL16.8
Cache / QueueRedis7
FrontendReact 18 / TypeScript-
Evaluation ServerPython (FastAPI)-
Container RuntimeDocker / Kubernetes-

Multi-tenancy model

VerifyWise implements schema-per-tenant isolation at the PostgreSQL level. Each organization receives a dedicated database schema derived from its organization ID:

  1. 1. The organization ID (integer) is hashed using SHA-256
  2. 2. The digest is encoded as base64
  3. 3. Non-alphanumeric characters are stripped
  4. 4. The result is truncated to exactly 10 characters

This produces a deterministic, collision-resistant schema identifier. All tenant data resides within its dedicated schema, and every query targets the tenant-specific schema qualified by the authenticated user's tenant hash.

Trust boundaries

Internet (untrusted)
TLS termination
Load balancer / Ingress controller

Application layer

Frontend
Nginx
Backend
Express
Eval server
FastAPI

Internal network only โ€” no direct internet access

PostgreSQL
16.8
Redis
7

All database and cache services communicate over an internal Docker bridge network and are not exposed to external traffic. The backend API is the sole gateway to data services.

Deployment models

Docker Compose

Defined in docker-compose.yml for development and small-scale deployments, with a production overlay in docker-compose.prod.yml. Secrets via .env file.

Kubernetes

Kustomize-based manifests with a dedicated verifywise namespace, PersistentVolumeClaims for PostgreSQL, and optional TLS Ingress via cert-manager. Secrets via Kubernetes Secrets.

Authentication & access control

JWT-based authentication, role-based authorization, and rate limiting

JWT dual-token pattern

VerifyWise uses a dual-token pattern. An access token is signed with JWT_SECRET using HMAC-SHA256 (HS256) and expires after 1 hour. A refresh token is signed with a separate REFRESH_TOKEN_SECRET and expires after 30 days, stored in an httpOnly cookie.

AttributeValue
httpOnlytrue (always)
path/api/users
expires30 days from issuance
securetrue in production, false otherwise
sameSitenone in production, lax otherwise

Password hashing

Passwords are hashed using bcrypt v6 with a cost factor of 10. The bcrypt library handles salt generation automatically. Password verification uses bcrypt.compare(), which performs constant-time comparison to mitigate timing attacks.

7-layer authentication middleware chain

Every authenticated request passes through the following sequential checks. If any layer fails, the request is rejected before reaching the controller.

1

Token presence

Extracts Bearer token from the Authorization header. Returns 400 if absent.

2

JWT signature verification

Verifies the token signature against JWT_SECRET using jsonwebtoken.verify(). Returns 401 if invalid.

3

Token expiration

Compares the expire timestamp in the payload against Date.now(). Returns 406 if expired.

4

Payload structure validation

Verifies id is a positive number and roleName is a non-empty string. Returns 400 if malformed.

5

Organization membership

Queries the database to confirm the user belongs to the claimed organization. Returns 403 if not a member.

6

Role consistency

Fetches the user's current role from the database and compares it to the token's roleName. Returns 403 if the role has changed since token issuance.

7

Tenant hash validation

Validates the tenant hash format using a regex, then recomputes the hash from the organization ID and compares. Returns 400 if mismatched.

Role-based access control

Role IDRole nameDescription
1AdminFull system access
2ReviewerReview and approval permissions
3EditorContent editing permissions
4AuditorRead-only audit access

Rate limiting

Four rate limiter tiers are implemented using express-rate-limit. Rate limit headers follow the IETF RateLimit-* standard.

LimiterWindowMax requestsApplied to
authLimiter15 minutes5Authentication endpoints
generalApiLimiter15 minutes100Standard API endpoints
fileOperationsLimiter15 minutes50File upload/download/delete
aiDetectionScanLimiter60 minutes10AI detection scan operations

Data protection

Encryption, tenant isolation, and SQL injection prevention

Encryption at rest

Sensitive fields (API keys, OAuth tokens, integration credentials) are encrypted using AES-256-CBC:

ParameterValue
Algorithmaes-256-cbc (128-bit block cipher with 256-bit key)
KeyDerived from ENCRYPTION_KEY env var, padded or truncated to exactly 32 bytes
Initialization vector16 random bytes generated via crypto.randomBytes(16) per encryption operation
Output format{iv_hex}:{ciphertext_hex} โ€” IV and ciphertext stored together, colon-delimited, both hex-encoded

Tenant data isolation

Tenant isolation is enforced at multiple layers:

1

Schema separation

Each tenant's data resides in a dedicated PostgreSQL schema created by createNewTenant(), which executes CREATE SCHEMA.

2

JWT-bound tenant hash

The tenant hash is embedded in the JWT payload at login and verified on every request.

3

Organization membership check

Every authenticated request verifies the user belongs to the claimed organization via a database query.

4

Tenant hash format validation

A regex (/^[a-zA-Z0-9]{10}$/) rejects any tenant identifier that does not match the expected format.

5

Identifier escaping

SQL queries that interpolate schema names use escapePgIdentifier(), which validates the identifier and double-quotes it.

SQL injection prevention

All database queries use parameterized statements via Sequelize's replacements option. Tenant schema names, which cannot be parameterized in PostgreSQL, are validated against the strict alphanumeric regex and escaped via escapePgIdentifier() before interpolation.

Secure sharing

Share links use 64-character hex tokens generated by crypto.randomBytes(32).toString("hex"). Token format is validated server-side using /^[a-f0-9]{64}$/. Share links support configurable field visibility and optional expiration dates.

Application security

Input validation, XSS prevention, file upload controls, and security headers

Input validation framework

VerifyWise implements a custom validation framework with type-specific validators, composed into schema objects and applied as Express middleware:

validateString()

Required, minLength, maxLength, regex pattern, empty string, whitespace trimming

validateNumber()

Required, min/max range, integer constraint, positive constraint

validateEnum()

Validates against allowed set, supports arrays

validateDate()

Valid date parsing, min/max date, future-only and past-only constraints

validateForeignKey()

Validates positive integer IDs for database foreign key references

XSS prevention

Frontend (DOMPurify)

Rich text content is sanitized with explicit allowlists:

  • Allowed tags: p, br, strong, b, em, i, u, h1-h6, blockquote, code, pre, ul, ol, li, a, img, span, div
  • Forbidden tags: script, object, embed, iframe, form, input, button
  • Forbidden attributes: onerror, onload, onclick, onmouseover, onfocus, onblur
  • URI validation: http, https, mailto, tel only

Backend (striptags)

HTML stripping is performed using the striptags library for content validation, such as checking that policy content contains meaningful text after tag removal.

File upload security

ControlImplementation
Size limit30 MB maximum, enforced by multer
MIME whitelistDocuments (PDF, DOC, DOCX, XLS, XLSX, CSV, MD), images (JPEG, PNG, GIF, WEBP, SVG, BMP, TIFF), videos (MP4, MPEG, MOV, AVI, WMV, WEBM, MKV)
Extension validationFile extension must match the declared MIME type
Filename sanitizationSpecial characters stripped, spaces replaced with underscores, length limited to 200 characters
StorageMemory storage (multer memoryStorage()), files stored as database BLOBs โ€” no temp files on disk
Access controlUpload and delete restricted to Admin, Reviewer, Editor via authorize() middleware
Rate limiting50 requests per 15 minutes via fileOperationsLimiter

CORS & security headers

CORS configuration

  • Origin must match HOST env var, localhost, 127.0.0.1, or ::1
  • Requests with no origin are permitted
  • credentials: true for cookie transmission
  • Allowed headers: Authorization, Content-Type, X-Requested-With

Security headers (helmet)

  • HSTS
  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • X-DNS-Prefetch-Control
  • Referrer-Policy

Infrastructure & deployment security

Docker configuration, Kubernetes security, and secrets management

Docker configuration

  • Backend container built from node:24-alpine3.20 (pinned version, not :latest)
  • Dependencies installed using npm ci for reproducible builds
  • Health checks defined for PostgreSQL (pg_isready) and Redis (redis-cli ping)
  • Backend depends on both services being healthy before starting

Kubernetes configuration

Namespace isolation

All resources deployed to the verifywise namespace.

Persistent storage

PersistentVolumeClaims for PostgreSQL data.

TLS Ingress

Optional cert-manager integration for automatic Let's Encrypt certificates, forced HTTPS redirect, and security headers.

Secrets management

Kubernetes Secrets for database credentials, JWT secrets, and encryption keys (base64-encoded, template provided).

Database & Redis security

PostgreSQL 16.8

Runs as an internal service with password authentication. Database port not exposed to host network โ€” only the backend accesses it via Docker's internal network.

Redis 7

Handles rate limiting and background job queues. Uses expose (internal only) rather than ports (host-mapped).

Logging, monitoring & auditability

Structured logging, audit trails, and access tracking

Application logging

VerifyWise uses Winston with DailyRotateFile for structured, tenant-specific application logging:

ConfigurationValue
File patternapp-YYYY-MM-DD.log
Max file size10 MB per file
Retention14 days (auto-rotated)
TimezoneUTC (forced)
DirectoryTenant-specific subdirectories
Production modeFile logging only (no console)
Development modeFile + colorized console output

Structured log format

{logId}, {timestamp (ISO 8601 UTC)}, {state}, {description}, {functionName}, {fileName}

Three logging functions enforce consistent usage: logProcessing() records the start of an operation, logSuccess() records successful completion, and logFailure() records errors.

Database audit trail

Non-read operations are automatically logged to the tenant-specific event_logs table:

ColumnDescription
event_typeCreate, Update, Delete, or Error
descriptionHuman-readable description of the event
user_idID of the user who performed the action

Software supply chain

Dependency scanning, CI/CD security checks, and base image pinning

Dependency scanning

Dependabot is configured for automated dependency scanning across all ecosystems:

EcosystemDirectoryFrequencyPR limit
npm (frontend)/ClientsWeekly (Monday)10
npm (backend)/ServersWeekly (Monday)10
pip (evaluation)/EvaluationModuleWeekly (Monday)5
GitHub Actions/Weekly (Monday)5
Docker (frontend)/ClientsWeekly-
Docker (backend)/ServersWeekly-

CI/CD security checks

Backend checks

  • npm audit --audit-level=high โ€” flags known vulnerabilities
  • TypeScript compilation (npm run build)
  • dependency-review-action โ€” blocks PRs with high-severity CVEs or restrictive licenses

Frontend checks

  • Equivalent npm audit and dependency review
  • Build checks for the frontend codebase

CI permissions

Both workflows use minimal GitHub token permissions: contents: read and security-events: write.

Base image pinning

Docker base images use pinned versions rather than floating tags:

  • Backend: node:24-alpine3.20
  • Database: postgres:16.8
  • Cache: redis:7

Secure development lifecycle

Code review, CI verification, secrets prevention, and vulnerability disclosure

Code review process

All code changes are submitted via pull requests targeting master or develop, triggering automated CI checks. Changes require review before merging.

CI verification

On every PR, the CI pipeline verifies TypeScript compiles without errors, npm audit reports no high-severity vulnerabilities, new dependencies don't introduce high-severity CVEs or restrictive licenses, and backend tests pass.

Secrets prevention

The .gitignore file excludes .env files from version control. The Kubernetes secrets example file documents the pattern for managing secrets without committing actual values.

Vulnerability disclosure

A SECURITY.md file exists at the repository root, directing reporters to create security advisories via GitHub's security advisory feature.

Implement secure AI governance

VerifyWise provides evidence-based security controls for AI risk management. Deploy on-premises or in your cloud with full control over your data and infrastructure.

Platform Security Overview | VerifyWise