Skip to content

feat: Sophos Central integration (Phase 1 — plumbing) - #232

Draft
ubercylon8 wants to merge 1 commit into
mainfrom
feat/sophos-integration-phase-1
Draft

feat: Sophos Central integration (Phase 1 — plumbing)#232
ubercylon8 wants to merge 1 commit into
mainfrom
feat/sophos-integration-phase-1

Conversation

@ubercylon8

Copy link
Copy Markdown
Collaborator

Summary

Phase 1 of the Sophos AV/EDR integration mirroring the existing Microsoft Defender integration. Ships credentials plumbing and a live OAuth2 + whoami connection test. Lays the foundation for phases 2–4 (sync, correlation, auto-resolve + dashboard).

Master plan: docs/sophos-integration-plan.md

What this PR adds

Backend public surface (/api/integrations/sophos):

  • GET — masked settings + discovered tenant/region/tier
  • POST — save credentials (partial update supported)
  • DELETE — disconnect (refuses when env-managed, mirroring Defender)
  • POST /test — real OAuth2 token + whoami round-trip, returns the discovered tenant ID, data-region URL, and tier

Internals:

  • SophosCentralClient with token caching, whoami bootstrap, 429/401 retry semantics matching MicrosoftGraphClient
  • IntegrationsSettingsService gains 7 new Sophos methods + AES-256-GCM encryption for client_id/client_secret
  • Env-var override (SOPHOS_CLIENT_ID, SOPHOS_CLIENT_SECRET, SOPHOS_TENANT_LABEL) — credentials only; discovered fields always come from a real whoami call
  • Full mirror in backend-serverless/ (async storage via Vercel Blob, encryption via HKDF)

Key design decisions

  1. No tenant_id input field — Sophos discovers the tenant via whoami, not operator-supplied. Including a tenant_id input would mislead users into typing one in only for it to be ignored.
  2. Peer module to Defender, not a refactorbackend/src/services/sophos/ mirrors backend/src/services/defender/ rather than introducing a shared VendorClient abstraction. Defender code is untouched.
  3. Parallel detection fields, not generalized — Phase 3 will add f0rtika.sophos_detected / f0rtika.sophos_stage_detected alongside the existing f0rtika.defender_detected, avoiding a hot re-enrichment of historical test docs.
  4. Phase 2+ fields declared nowlast_alert_sync, auto_resolve_mode etc. live in SophosIntegrationSettings from day one so future phases don't require an integrations.json migration.

Tests

Suite New Total Status
backend (Docker) 33 1254 ✅ all green
backend-serverless (Vercel) 11 761 / 762 ✅ Sophos green; 1 pre-existing flake in schedules.service.test.ts (commit f5749e5, unrelated)
frontend build ✅ clean (no UI changes yet — Phase 4)

The new tests cover: token cache hit/miss, whoami parsing, dataRegion trailing-slash normalization, 429 honoring Retry-After, 401 token-refresh once, partner/organization idType rejection, encryption round-trip, env-var override precedence, env-deletion refusal, route happy paths, route auth-failure paths.

What this PR does NOT do

  • No background sync — Phase 2 will add the achilles-sophos ES index, alert/endpoint sync, and Vercel Cron wiring
  • No correlation — Phase 3 will write f0rtika.sophos_detected flags on test docs
  • No auto-resolve — Phase 4 will add allowedActions-aware alert resolution
  • No frontend — Phase 4 will add the SophosConfig settings card and SophosTab analytics widget

Test plan

  • cd backend && npm test — full suite green (1254 tests)
  • cd backend-serverless && npm test — full suite green except known pre-existing schedule flake
  • cd backend && npm run build — clean
  • cd backend-serverless && npm run build — clean
  • cd frontend && npm run build — clean
  • Smoke: ./scripts/start.sh -k --daemon and curl -X POST localhost:3000/api/integrations/sophos/test -H 'Content-Type: application/json' -d '{}' returns deterministic "Missing credentials" JSON
  • (Optional, requires real Sophos creds) End-to-end /test against a real tenant returns success: true with the right data_region and tier

🤖 Generated with Claude Code

Phase 1 of the Sophos AV/EDR integration mirroring the Defender integration.
Ships credentials plumbing + a live OAuth2 + whoami connection test. No
background sync yet — phases 2-4 will add ingestion, correlation, and
auto-resolve.

Plan: docs/sophos-integration-plan.md

Public surface:
- GET    /api/integrations/sophos       — masked settings + discovered metadata
- POST   /api/integrations/sophos       — save credentials (partial update)
- DELETE /api/integrations/sophos       — disconnect (refuses when env-managed)
- POST   /api/integrations/sophos/test  — real token + whoami round-trip,
                                          returns discovered tenant/region/tier

Sophos differs from Defender in that only client_id/client_secret are
operator-supplied; tenant_id, data_region, and tier are discovered via
Sophos's whoami endpoint and cached in settings. Env-var override
(SOPHOS_CLIENT_ID / SOPHOS_CLIENT_SECRET) covers credentials only —
discovered fields always come from a real whoami call.

44 new tests (33 backend, 11 backend-serverless). Full suites green
(backend 1254/1254; backend-serverless 1 pre-existing flake in
schedules.service unrelated to this change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
projectachilles-io Ready Ready Preview, Comment May 12, 2026 11:51pm
wiki Ready Ready Preview, Comment May 12, 2026 11:51pm

@github-actions

Copy link
Copy Markdown

Semgrep SAST Results

No security findings. All checks passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant