Skip to content

Repository files navigation

PurpleSentinelStudio

Sec Ops Studio, an all in one SOC toolkit.

Purple Sentinel Studio is a fullstack analyst workspace for:

  • IOC intake and tracking
  • OSINT-style enrichment with attribution
  • Quick utilities (IP range, Geo-IP, hashing, JSON prettifier, timeline builder)

Stack

  • React + Vite (TypeScript)
  • Node + Express (TypeScript)
  • PostgreSQL

UI Sections

  • /login Secure authentication entrypoint
  • / Overview dashboard
  • /iocs IOC Vault
  • /tools Quick Tools workspace (selector-based)
  • /feeds Threat Feeds workspace
  • /admin Administration workspace (user accounts + audit trail, administrator role)
  • /incidents Incident Manager (declare incident IDs and metadata)
  • /timeline-builder Dedicated Timeline Builder workspace
  • /incident-reports Incident report generation workspace
  • /incident-graph Dedicated graph view (opened in a new tab from Incident Reports)

Quickstart

Purple Sentinel SOC Studio is designed to run only via containers (nginx + backend services + Postgres).

cd /PurpleSentinelStudio
cp .env.example .env

# Recommended: set strong secrets (examples)
echo "JWT_SECRET=\"$(openssl rand -hex 64)\"" >> .env
echo "PSS_MASTER_KEY=\"$(openssl rand -base64 32)\"" >> .env
echo "DEFAULT_ADMIN_PASSWORD=\"change-this-to-a-strong-password\"" >> .env

docker compose up -d --build --remove-orphans

Open the UI:

HTTP (redirects to HTTPS):

Health check:

curl -sk https://localhost:8443/api/health

Docs:

  • containerisation.md (deploy/run guide)
  • CONTAINER_INFRA.md (architecture + routing)

Container Services

In Docker Compose, nginx routes API traffic by path:

  • /api/auth/*, /api/profile/*, /api/audit/*, /api/settings/*auth-service (:4001)
  • /api/iocs/*, /api/ioc-enrichment/*ioc (:4005)
  • /api/tools/*tools (:4010)
  • everything else under /api/*core-api (:4000)

Shared Express Bootstrap

To prevent drift across backend services, common Express setup now lives in:

  • server/src/utils/expressBootstrap.ts

This module centralizes:

  • standard middleware bootstrap (helmet, cors, JSON body limit, morgan, trust proxy handling)
  • shared authenticated mutating-request audit middleware
  • shared 404 and error-response handlers
  • consistent SIGTERM/SIGINT graceful shutdown registration so each service drains HTTP traffic and closes PostgreSQL cleanly

All Node services (core-api, auth-service, ioc, tools) import this module so middleware behavior and error handling remain consistent.

API Endpoints

When running via Docker Compose, all API calls go through nginx at:

  • https://localhost:8443/api/... (self-signed)

  • GET /api/health core API health check

  • GET /api/health/intel intel provider reachability probe

  • GET /api/health/components aggregated component availability (administrator)

  • POST /api/auth/login sign in and receive access token (token_type is DPoP when a dpop_jwk is supplied; otherwise Bearer)

  • POST /api/auth/mfa/complete complete MFA login challenge and receive access token

  • POST /api/auth/logout sign out (audited)

  • GET /api/auth/me get current authenticated user profile + permissions

  • GET /api/auth/mfa/status get current account MFA eligibility/status

  • POST /api/auth/mfa/enroll/start begin TOTP enrollment and return QR payload

  • POST /api/auth/mfa/enroll/verify verify TOTP enrollment code and enable MFA

  • POST /api/auth/mfa/disable disable MFA for current account (requires password + TOTP)

  • GET /api/profile/me get the current user's directory profile (full name, email, avatar)

  • PUT /api/profile/me update the current user's profile avatar (avatar_data_url); full name/email are administrator-managed

  • GET /api/profile/users list active user directory profiles (authenticated users)

  • GET /api/profile/preferences get per-user UI preferences (sound + accent theme)

  • PUT /api/profile/preferences update per-user UI preferences

  • GET /api/auth/users list user accounts (administrator, paginated)

    • Query params: search, limit (default 5), offset
    • Response: { users, total, limit, offset }
  • POST /api/auth/users create user account (administrator): username, display_name, role, user_type (application_user or api_bot), password

  • PATCH /api/auth/users/:id update user account + directory profile (administrator): username, display_name (full name), role, is_active, password, email, avatar_data_url (note: user_type is configured at creation)

  • DELETE /api/auth/users/:id delete user account (administrator)

  • GET /api/auth/roles list roles + permissions (administrator)

  • POST /api/auth/roles create role (administrator)

  • PATCH /api/auth/roles/:name update role permissions/metadata (administrator)

  • DELETE /api/auth/roles/:name delete role (administrator; non-system roles only)

  • GET /api/iocs list IOCs (supports q, type, status, severity, enrichment_status; include limit + offset for pagination, returning { iocs, total, limit, offset })

  • POST /api/iocs create IOC

  • GET /api/iocs/:id IOC detail + enrichment

  • PATCH /api/iocs/:id update IOC

  • DELETE /api/iocs/:id remove IOC

  • POST /api/iocs/:id/enrich run enrichment and attribution (legacy alias)

  • GET /api/ioc-enrichment/:iocId get enrichment snapshot + attribution for one IOC

  • GET /api/ioc-enrichment/:iocId/attribution get attribution-only payload

  • POST /api/ioc-enrichment/:iocId/run run enrichment for one IOC and persist results

  • GET /api/ioc-enrichment/pending/status get pending IOC enrichment count

  • POST /api/ioc-enrichment/pending/run process pending IOC enrichment in batch (limit 1-200)

  • GET /api/settings/integrations list enrichment integration settings and key status

  • GET /api/settings/integrations/:provider get one integration setting

  • PUT /api/settings/integrations/:provider set integration enabled state and/or API key

  • DELETE /api/settings/integrations/:provider/key remove a stored API key and disable provider

  • GET /api/audit/events retrieve audit trail events (administrator, paginated)

    • Query params: limit (default 200, max 500), offset
    • Response: { events, total, limit, offset }
  • GET /api/incidents list incidents

  • GET /api/incidents/:incidentId incident detail

  • POST /api/incidents declare incident

  • PATCH /api/incidents/:incidentId update incident metadata/status

  • DELETE /api/incidents/:incidentId delete incident (cascades timeline entries)

  • GET /api/tools/ip-range?cidr= CIDR calculator

  • GET /api/tools/geoip?ip= Geo-IP lookup

  • GET /api/tools/threat-feeds curated open-source threat feed catalog

  • POST /api/tools/hash hashing utilities

  • POST /api/tools/url-parse URL/domain parser

  • GET /api/tools/dns?name=&type= DNS lookup

  • POST /api/tools/encode base64/url encode/decode

  • POST /api/tools/json-prettify JSON pretty/minified formatter + flattened key paths

  • POST /api/tools/timestamp timestamp converter

  • POST /api/tools/extract-iocs regex IOC extraction

  • GET /api/tools/http-requests list stored outbound HTTP request history (per user)

  • GET /api/tools/http-requests/:id retrieve one stored request + response (per user)

  • POST /api/tools/http-requests execute + store an outbound HTTP request (audited; sensitive headers are redacted at rest)

  • POST /api/tools/entropy password entropy estimate

  • POST /api/tools/magic file signature detection

  • POST /api/tools/jwt-decode decode JWT header/payload

  • POST /api/tools/whois RDAP/ASN lookup

  • POST /api/tools/subnet-overlap CIDR overlap checker

  • POST /api/tools/cidr-summarize CIDR/IP summarizer

  • POST /api/tools/defang IOC defang/refang

  • POST /api/tools/stix-pattern STIX pattern generator

  • POST /api/tools/sigma-rule Sigma YAML generator

  • POST /api/tools/pcap-filter Wireshark/tcpdump filter snippets

  • POST /api/tools/mitre-map tag-to-ATT&CK mapper

  • POST /api/tools/risk-score weighted risk calculator

  • GET /api/tools/timeline-entries?incident_id=&limit= incident timeline entries

  • POST /api/tools/timeline-entries create a structured timeline entry

  • DELETE /api/tools/timeline-entries/:id delete a timeline entry

  • POST /api/tools/timeline-normalize event timeline normalizer

  • POST /api/tools/http-headers-analyze HTTP header analyzer

  • POST /api/tools/tls-inspect TLS certificate inspector

  • POST /api/tools/user-agent-parse user-agent parser

  • POST /api/tools/email-headers-analyze email header analyzer

  • POST /api/tools/file-anomaly filename anomaly detection

  • POST /api/tools/regex-test regex tester with presets

Threat Feeds API

  • Endpoint: GET /api/tools/threat-feeds
  • Query params (optional): category, format, access, search
  • Returns:
  • feeds list of curated open-source feeds (public and registration-based)
  • categories, formats, access_levels for filter controls

IOC Enrichment Service API

  • Base route: /api/ioc-enrichment
  • Purpose: Dedicated service for enrichment and attribution of IOCs stored in PostgreSQL.
  • Storage behavior:
  • writes provider outputs into enrichments
  • updates IOC fields in iocs (enrichment_status, attribution, last_enriched_at, updated_at)
  • mode behavior:
  • uses live provider APIs when enabled keys are configured
  • gracefully falls back to deterministic mock enrichment if no live provider succeeds
  • Example batch request:
{
  "limit": 50
}

Integration Settings API

  • Base route: /api/settings/integrations
  • Supported providers:
  • virustotal
  • abuseipdb
  • otx (AlienVault OTX)
  • shodan
  • API keys are encrypted before storage in PostgreSQL and only masked hints are returned to the frontend.
  • Optional env vars:
  • PSS_MASTER_KEY (preferred): 32-byte secret (hex or base64) for API-key encryption
  • SETTINGS_KEY_PATH: override path for generated local encryption key file

Authentication and RBAC

  • Authentication model: JWT access token
  • Optional MFA: TOTP (Google Authenticator-compatible) for application_user accounts. Enrollment returns an otpauth:// URI and QR code image; MFA is completed with a 6-digit code before token issuance.
  • Default browser flow uses a DPoP-style proof-of-possession handshake (nonce + per-request signature) to reduce bearer token replay if a JWT is stolen.
  • Login accepts optional dpop_jwk (public P-256 JWK). When supplied, the server binds the JWT to that key via cnf.jkt and returns token_type: "DPoP" plus an initial nonce (dpop_nonce, also mirrored in the DPoP-Nonce response header).
  • If MFA is enabled for the account, POST /api/auth/login returns { mfa_required, challenge_id, challenge_expires_at }; the client then submits POST /api/auth/mfa/complete with the challenge + TOTP code to obtain the JWT.
  • DPoP requests use Authorization: DPoP <token> plus a DPoP: <proofJWT> header containing a signed proof with htm, htu, iat, jti, and nonce.
  • The server rotates nonces via DPoP-Nonce on successful requests and the client automatically updates and retries once on 401 when it receives a fresh nonce.
  • Bearer tokens remain supported for non-browser clients: omit dpop_jwk and use Authorization: Bearer <token>.
  • For a detailed breakdown of JWT behavior and where it is enforced, see JWT.md.
  • Role-based access control enforced in backend and frontend route guards.
  • Role-based access control is enforced by the backend on every request using the role-permission mapping stored in PostgreSQL (roles table).
  • Access token lifetime is short by default (15 minutes) and is refreshed automatically during active use via X-PS-Auth-Token response headers. Configure via JWT_TTL_SECONDS and JWT_REFRESH_WINDOW_SECONDS.
  • The browser client enforces an inactivity timeout (default 15 minutes) and clears local auth state. Configure via VITE_IDLE_TIMEOUT_MS or VITE_IDLE_TIMEOUT_MINUTES.
  • Default roles are seeded on startup (administrator, incident_commander, soc_analyst, threat_intel, forensics, responder, viewer) and can be extended/modified via GET/POST/PATCH /api/auth/roles.
  • On first start, a default administrator is auto-created if no admin exists:
  • username from DEFAULT_ADMIN_USERNAME (default admin)
  • password from DEFAULT_ADMIN_PASSWORD if provided (minimum 12 chars), otherwise generated at startup and printed to server logs
  • Optional token signing config:
  • JWT_SECRET (recommended for stable JWT signing secret)
  • JWT_SECRET_PATH (path for generated signing secret when JWT_SECRET is not set)

Audit Trail

  • Every authenticated mutating API call (POST, PUT, PATCH, DELETE) is recorded in audit_events.
  • Auth login success/failure and logout are also audited.
  • Audit logs are viewable via /api/audit/events and the Administration UI for administrator users only.

Timeline Builder API

  • Endpoints:

  • GET /api/tools/timeline-entries?incident_id=IR-2026-0001&limit=250

  • POST /api/tools/timeline-entries

  • DELETE /api/tools/timeline-entries/:id

  • Purpose: Store incident timeline events as structured JSON in PostgreSQL (timeline_entries table).

  • Requirement: Incident must exist in Incident Manager (incidents table) before timeline entries can be created or loaded.

  • Entry model supports:

  • entry_type for system events, analyst actions, communications, and stage transitions

  • irp_stage and optional transition_to_stage for IRP phase changes

  • operational metadata: source system, actor, role, channel, audience, case reference, tags, evidence refs

  • metadata object for custom structured fields

  • Example create request:

{
  "incident_id": "IR-2026-0001",
  "occurred_at": "2026-02-11T14:12:00Z",
  "entry_type": "communication",
  "irp_stage": "containment",
  "transition_to_stage": "eradication",
  "title": "IR lead approved transition to eradication",
  "summary": "Containment validation completed; eradication starts immediately.",
  "source_system": "IR coordination",
  "actor_name": "Jane Analyst",
  "actor_role": "IR Lead",
  "communication_channel": "War room bridge",
  "audience": "SOC lead, IT ops lead, legal",
  "ticket_ref": "INC-2026-0198",
  "tags": ["irp", "stage-transition", "communications"],
  "evidence_refs": ["ticket://INC-2026-0198", "notes://warroom/2026-02-11"],
  "metadata": {
    "approval": "granted",
    "change_window": "immediate"
  }
}

JSON Prettifier API

  • Endpoint: POST /api/tools/json-prettify
  • Request body:
{
  "value": "{\"ioc\":\"8.8.8.8\",\"tags\":[\"dns\",\"beacon\"],\"meta\":{\"source\":\"hunt\"}}",
  "indent": 2
}
  • Response includes:
  • formatted (pretty JSON)
  • minified (single-line JSON)
  • keys (flattened key paths in dot notation, including arrays with [], e.g. tags[], meta.source)

Incident Manager API

  • Endpoint: POST /api/incidents
  • Purpose: Declare incidents with canonical incident_id plus core metadata.
  • Example request:
{
  "incident_id": "IR-2026-0001",
  "title": "Potential ransomware activity on finance subnet",
  "status": "declared",
  "severity": "high",
  "started_at": "2026-02-11T14:00:00Z",
  "declared_by": "SOC Analyst",
  "comms_channel": "Slack #ir-warroom",
  "summary": "Initial declaration after correlated endpoint alerts.",
  "metadata": {
    "business_unit": "Finance",
    "region": "US"
  }
}

Notes

  • IOC enrichment supports live provider APIs with configurable keys from Settings.
  • If no live provider is enabled or reachable, enrichment falls back to deterministic mock OSINT data.
  • Database persistence is provided by PostgreSQL (see .env.example and docker-compose.yml).
  • Geo-IP lookups use the public ipapi.co endpoint by default.
  • Security docs: JWT.md (auth + DPoP binding), ASVS.md (ASVS Level 2 gap assessment), DATASTRUCTURE.md (DB schema + ERD)

Changelog

  • See CHANGELOG.md for release history.

About

Sec Ops Studio, an all in one SOC toolkit

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages