-
Notifications
You must be signed in to change notification settings - Fork 0
Security
Security is a first-class concern: DataShield handles employee PII and third-party credentials. This page summarizes the controls in the codebase and the CI pipeline.
Directory configs, breach-provider API keys, and webhook URLs are encrypted
with AES-256-GCM before they touch the database
(src/lib/directory/crypto.ts):
- The 32-byte key is derived from
DIRECTORY_ENCRYPTION_KEYvia SHA-256, so any sufficiently long secret normalizes to a valid key. - A fresh 12-byte random IV (the recommended GCM nonce) is generated per
encryption; the stored blob is
base64(iv || authTag || ciphertext). - The 16-byte GCM auth tag is verified on decrypt, so tampered ciphertext fails closed.
- No silent fallback: a missing or under-32-character key throws immediately rather than degrading to plaintext.
Only display-safe hints are stored unencrypted: keyHint (key suffix) and
urlHint (webhook host only).
- Better Auth issues and validates sessions, stored in the
Sessiontable so they can be listed and revoked server-side. - Passwords are hashed with
bcryptjs, set explicitly rather than using the library default. - Second factors: TOTP with backup codes and lockout, email OTP, and passkeys
(WebAuthn). A company picks which it accepts via
allowedAuthMethods. - SSO over OIDC, one provider per company, with domain verification. When
ssoMandatoryis on, local sign-in is refused except for users flaggedssoExempt, the deliberate anti-lockout valve. - OIDC client configuration is encrypted at rest through a Prisma extension.
- Invitation tokens are single-use, stored hashed, and expire after 72 hours.
-
src/middleware.tsprotects every route exceptapi/auth, static assets, and/login.
See Authentication.
All API authorization goes through one module (apiAuth.ts) with two guards,
requireAuth and requirePermission. Authorization is permission-based over a
frozen vocabulary of 36 permissions; the old ADMIN / VIEWER enum is gone. A
coverage test fails the build if a mutating route is not mapped to a permission.
Queries are always scoped by companyId so tenants are isolated.
Three controls limit privilege escalation:
- No-escalation subset rule: you can only grant permissions you hold.
-
Crown jewels:
roles:manage,users:manage,sso:configandsso:role_mapadditionally require a fresh step-up re-authentication. -
Step-up grants last 5 minutes and are stored in
StepUpGrant.
Last-admin protection refuses any change that would leave the company with
nobody holding roles:manage. See Roles and Permissions.
Role, user, invitation and SSO changes are written append-only to AuditLog
with actor, before/after JSON snapshots and source IP. There is a single writer
and no update or delete path. See Audit Log.
Inbound SCIM requests authenticate with a per-connection bearer token compared
in constant time (crypto.timingSafeEqual) to neutralize timing attacks.
Failures (missing token, unknown connection, unreadable config) all return
401, never 500. See SCIM Provisioning.
src/lib/rateLimit.ts is a fixed-window counter backed by PostgreSQL (the
RateLimit and ApiRateLimit tables) rather than a per-process map, so the
limit holds across instances. Scans are capped at 5 per company per minute; the
data API and the SIEM feed have their own windows. Expired rows are swept on
write.
Webhook dispatch (src/lib/webhooks.ts) only fires for events at or above each
webhook's minSeverity, decrypts URLs only at send time, and swallows network
errors so a dead endpoint never breaks a scan. Target URLs are validated against
SSRF (src/lib/ssrf.ts) before they are stored, so a webhook cannot be pointed
at internal addresses to make the server probe its own network. See
Notifications.
Baseline headers are set on every response in next.config.ts
(X-Frame-Options: DENY, X-Content-Type-Options: nosniff), plus a strict
Content-Security-Policy with a per-request nonce (src/lib/csp.ts).
Remediation acts on the customer's live IdP (revoke sessions, force password
resets) and is off by default (Company.remediationEnabled). The endpoint
refuses with 403 until it is deliberately enabled, every attempt is recorded
append-only in RemediationAction, and a directory type can only run the actions
its API supports. See Remediation.
The SIEM pull feed authenticates with a per-company bearer token, stored
encrypted (AES-256-GCM) and compared in constant time; a missing or unreadable
token returns 401. The feed is rate limited (60/min) and capped (1000 alerts).
See SIEM Integration.
Three workflows enforce security on every PR and push (see Development):
-
Security (
security.yml):npm audit --audit-level=high, Gitleaks and TruffleHog secret scanning over full history, and Dependency Review on PRs. Runs weekly on a schedule to catch newly disclosed advisories. -
CodeQL (
codeql.yml): static analysis. -
Compliance (
compliance.yml): PR title and content checks, ASCII-only enforcement, dependency advisory checks.
Use the policy in .github/SECURITY.md in the repository. Do not open a public
issue for security reports.
DataShield is source-available software by Melvin PETIT (WhiteMuush). Live demo, read only, no signup.
Getting started
Architecture
Features
- Breach Scanning
- Risk Scoring
- Directory Integrations
- MFA Coverage
- SCIM Provisioning
- Dashboard and Widgets
- Reports
- Exposure Register
Integrations
Identity and access
Reference
Contributing