Accans

Security posture

Application-, container-, and CI/CD-layer security practices for the Digital Sovereignty Heatmap.

Contenu en anglais — la navigation de la page reste en français.

Security Posture

This project follows security best practices across application, container, and CI/CD layers.

Current Measures

HTTP Security Headers

Applied via Next.js middleware (src/middleware.ts) to all routes:

Header Value
Content-Security-Policy default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.gstatic.com; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
X-Frame-Options DENY
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy camera=(), microphone=(), geolocation=()

API routes additionally receive Cache-Control: no-store.

Container Security

  • Multi-stage Docker build — production image contains only runtime dependencies
  • Non-root user (nextjs, UID 1001) in the runtime stage
  • Read-only root filesystem (read_only: true in docker-compose)
  • Resource limits: 512 MB memory, 1 CPU
  • Restrictive data directory permissions (750)
  • Alpine-based images for minimal attack surface
  • OCI image labels for provenance
  • Healthcheck built into the container

CI/CD Security

  • Trivy — container image vulnerability scanning (CRITICAL + HIGH)
  • gitleaks — secret detection in git history
  • pnpm audit — dependency vulnerability scanning
  • Minimal GitHub Actions permissions (contents: read for CI)
  • Release workflow scans image before pushing to GHCR

Application Security

  • Environment-based configuration (no secrets in code)
  • .env files excluded from version control
  • Request body size limits on every POST endpoint
  • Rate limiting on POST endpoints (in-memory token bucket, per client IP; limits are deliberately not published)
  • Zod schema validation on all POST request bodies
  • No prompt payloads logged in Claude integration

Telemetry & Privacy

Telemetry is on by default (opt-out): only an explicit TELEMETRY_ENABLED=false disables it server-side, and visitors can switch it off per browser in the footer. This is defensible because nothing that is collected qualifies as personal data. The following privacy guarantees apply:

Guarantee Implementation
No raw events stored Server increments aggregated counters only (metrics_counters table)
Strict allowlists Every telemetry field is validated against a fixed enum. The one free-text input, application names from the list import that the catalogue does not recognise, is sanitised (anything resembling an e-mail address, URL or number is dropped) and counted per name without any other context
No IP addresses stored Client IP is used for rate limiting only (in-memory, never persisted)
No personal data No usernames, emails, sessions, or device fingerprints
Opt-out on both layers Server: on unless TELEMETRY_ENABLED=false. User: on unless switched off in the footer; the explicit choice is stored in localStorage (no cookie)
k-anonymity Public /trends page only shows buckets above a configurable minimum count; smaller buckets are grouped as "Other"
Score bands only Raw scores (0-100) are bucketed into 3 bands (0-33, 34-66, 67-100) before transmission

What is collected (when both server and user opt in):

  • Event type: assessment_completed, vendor_selected, memo_generated
  • Score band (not raw score)
  • Category and vendor slug (from curated allowlist only)
  • Context enum values (sector, data classification, criticality, EU residency)

What is NOT collected:

  • IP addresses, user agents, or device identifiers
  • Raw assessment scores
  • Free-text input of any kind
  • Timing data, session identifiers, or referrer information

Reporting Vulnerabilities

If you discover a security vulnerability, please report it responsibly by opening a private security advisory on the GitHub repository.

Future Considerations

  • Authentication & authorisation strategy
  • CORS policy for cross-origin API access
  • Dependency update automation (Dependabot / Renovate)
← Retour à la documentationCe document est un instantané statique de la source canonique dans notre base de code. Les mises à jour sont publiées à chaque nouvelle build.